C-код работает на archlinux, но не на Ubuntu 10.04
Я работал с другом, чтобы написать некоторый код, и он в основном был протестирован на archlinux. Когда мы пришли к перекомпиляции и тестированию в Ubuntu, он проходит через программу, а затем возвращает ошибку разрушения стека. Я добавил несколько утверждений для печати, чтобы выяснить, где происходит сбой, но ни один из нас не может увидеть причину.
Код такой, как показано ниже, ошибка, кажется, происходит, когда код возвращается из функции "authenticate":
/** Includes **/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>
#include <pthread.h>
#include <mysql.h>
/* Definitions */
#define LIC_SERVER_PORTNO 2325
//#define LIC_SERVER_ADDR "69.68.67.66"
#define SERVER_PORTNO 4959
#define UPLEN 45
#define MAX_USER_NUM 100
//#define MAX_KA_TIME 2000
#define MAX_KA_TIME 20
/** Structure to hold info on logged in users **/
typedef struct {
char *ip;
time_t login_time;
time_t last_check;
} user;
/** GLOBALS **/
user *users;
int user_num;
/** Version string function **/
const char* version()
{
return "0.1r3";
}
void exit_signal(int sig)
{
pthread_exit(NULL);
printf("\nTerminating server. Goodbye!\n\n");
(void) signal(SIGINT, SIG_DFL);
exit(0);
}
/** Function for error reporting **/
void error(char *msg)
{
perror(msg);
exit(1);
}
int authenticate(char* uname,char* pwd)
{
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
int r=0;
const char *server = "localhost";
const char *user = "root";
const char *password = "12345"; /* set me first */
const char *database = "test";
printf("inside authenticate %s\n","one");
conn = mysql_init(NULL);
/* Connect to database */
if (!mysql_real_connect(conn, server,user, password, database, 0, NULL, 0)) {
fprintf(stderr, "%s\n", mysql_error(conn));
exit(1);
}
printf("inside authenticate %s\n","two");
/* send SQL query */
char query [] = "select * from users where username='";
char q2 [] = "';";
//char *tmp,*query;
strcat(query,uname);
strcat(query,q2);
printf("inside authenticate %s\n","three");
if (mysql_query(conn,query)) {
fprintf(stderr, "%s\n", mysql_error(conn));
exit(1);
}
res = mysql_use_result(conn);
if (res==NULL) return 0;
row = mysql_fetch_row(res);
if (row==NULL) return 0;
printf("inside authenticate %s\n","four");
if ( (strcmp(row[2],uname)==0) && (strcmp(row[3],pwd)==0) )
r = 1;
/* close connection */
mysql_free_result(res);
mysql_close(conn);
printf("inside authenticate %s\n","five");
return r;
}
/** Keep alive guard **/
void *keep_alive_guard(void *thread_data)
{
int i,k,c;
time_t ts;
while (1) {
ts = time(NULL);
for (i=0;i<user_num;i++) {
char the_ip[50],the_ip2[50];
char scall1[1024],scall2[1024];
if (ts-users[i].last_check>MAX_KA_TIME) {
strncpy(the_ip,users[i].ip,50);
strncpy(the_ip2,users[i].ip,50);
// system call to delete redirection for all incoming packets to licence server for port 1055
strncpy(scall1,"iptables -t nat -D PREROUTING -s ",1024);
strcat(scall1,the_ip);
strcat(scall1," -j REDIRECT -p tcp --to-port 1055 --dport 4957");
//printf("%s\n",scall1);
FILE *phony = popen(scall1,"r");
pclose(phony);
// system call to delete redirection for all incoming packets to licence server for port 2325
strncpy(scall2,"iptables -t nat -D PREROUTING -s ",1024);
strcat(scall2,the_ip2);
strcat(scall2," -j REDIRECT -p tcp --to-port 2325 --dport 4958");
//printf("%s\n",scall2);
FILE *phony2 = popen(scall2,"r");
pclose(phony2);
for (k=i;k<user_num;k++) {
if (k!=MAX_USER_NUM-1) {
memcpy(&users[k],&users[k+1],sizeof(user));
}
}
user_num--;
}
}
}
}
/* Main function */
int main(int argc,char* argv[])
{
// Initializing
printf("Licence proxy (server) v%s\n",version());
printf("Initializing...");
user_num = 0;
users = (user*)malloc(MAX_USER_NUM*sizeof(user));
(void) signal(SIGINT, exit_signal);
FILE *notha = popen("iptables -A INPUT ! -s 127.0.0.1 -p tcp --dport 1055 -j DROP","r");
pclose(notha);
FILE *notha2 = popen("iptables -A INPUT ! -s 127.0.0.1 -p tcp --dport 2325 -j DROP","r");
pclose(notha2);
// Basic variables for socket handling
int sockfd, newsockfd, portno, clilen;
struct sockaddr_in serv_addr, cli_addr; //addresses
int n,i;
// Create socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0)
error((char*)"ERROR opening socket");
bzero((char *) &serv_addr, sizeof(serv_addr));
portno = SERVER_PORTNO; // Set port number to listen
// Set options for socket and bind to port
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = INADDR_ANY;
serv_addr.sin_port = htons(portno);
if (bind(sockfd, (struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error((char*)"ERROR on binding");
printf("ok!\n\nRunning...\n\n");
// Start listening
listen(sockfd,5);
// Create thread for keep alive guard
pthread_t kag;
int rc;
rc = pthread_create(&kag,NULL,keep_alive_guard,(void *)user_num);
char datauser [UPLEN],*user;
char datapass [UPLEN],*pass;
char datareq_id [UPLEN],*req_id;
char respOK [] = "OK";
char respNK [] = "NK";
while (1) {
// Accept connection from peer
clilen = sizeof(cli_addr);
newsockfd = accept(sockfd,(struct sockaddr *) &cli_addr,(socklen_t*)&clilen);
if (newsockfd < 0)
error((char*)"ERROR on accept");
n = recv(newsockfd,datareq_id,UPLEN,0);
if (n < 0) error((char*)"ERROR reading username from client");
req_id = (char*)malloc(strlen(datareq_id));
strncpy(req_id,datareq_id,strlen(datareq_id));
if (req_id[0]=='K') {
for (i=0;i<user_num;i++) {
if (strcmp(users[i].ip,inet_ntoa(cli_addr.sin_addr))==0) {
users[i].last_check = time(NULL);
}
}
}
else if (req_id[0]=='L') {
printf("Transaction requested from %s\n",inet_ntoa(cli_addr.sin_addr));
// Read and wrap username from client
n = recv(newsockfd,datauser,UPLEN,0);
if (n < 0) error((char*)"ERROR reading username from client");
user = (char*)malloc(strlen(datauser));
strncpy(user,datauser,strlen(datauser));
printf("here %s\n","one");
// Read and wrap password from client
n = recv(newsockfd,datapass,UPLEN,0);
if (n < 0) error((char*)"ERROR reading password from client");
pass = (char*)malloc(strlen(datapass));
strncpy(pass,datapass,strlen(datapass));
printf("here %s\n","two");
char scall[1024],scall2[1024];
printf("here %s\n","two + one line");
//Cross-check authorization data against MYSQL database and take action
if (authenticate(user,pass)==1) {
printf("outside authenticate (success) %s\n","one");
n = send(newsockfd,respOK,UPLEN,0);
printf("outside authenticate (success) %s\n","two");
if (n < 0) error((char*)"ERROR writing to socket");
printf("here %s\n","two and a lot");
// system call to redirect all incoming packets to licence server for port 1055
strncpy(scall,"iptables -t nat -A PREROUTING -s ",1024);
printf("here %s\n","three");
strcat(scall,inet_ntoa(cli_addr.sin_addr));
strcat(scall," -j REDIRECT -p tcp --to-port 1055 --dport 4957");
//printf("%s\n",scall);
FILE *phony = popen(scall,"r");
pclose(phony);
printf("here %s\n","four");
// system call to redirect all incoming packets to licence server for port 2325
strncpy(scall2,"iptables -t nat -A PREROUTING -s ",1024);
strcat(scall2,inet_ntoa(cli_addr.sin_addr));
strcat(scall2," -j REDIRECT -p tcp --to-port 2325 --dport 4958");
//printf("%s\n",scall2);
FILE *phony2 = popen(scall2,"r");
pclose(phony2);
printf("here %s\n","five");
printf("%s successfully logged in.\n",inet_ntoa(cli_addr.sin_addr));
users[user_num].ip = (char*)malloc(strlen(inet_ntoa(cli_addr.sin_addr)));
strncpy(users[user_num].ip,inet_ntoa(cli_addr.sin_addr),strlen(inet_ntoa(cli_addr.sin_addr)));
printf("here %s\n","six");
users[user_num].login_time = time(NULL);
users[user_num].last_check = time(NULL);
user_num++;
}
else
{
printf("outside authenticate (fail) %s","one");
n = send(newsockfd,respNK,UPLEN,0);
if (n < 0) error((char*)"ERROR writing to socket");
printf("Access denied to %s.\n",inet_ntoa(cli_addr.sin_addr));
}
free(user);
free(pass);
}
close(newsockfd);
}
shutdown(sockfd,SHUT_RDWR);
close(sockfd);
pthread_exit(NULL);
return 0;
}
Местоположение ошибки дано вне консольного вывода ниже:
Transaction requested from 192.168.1.10
here one
here two
here two + one line
inside authenticate one
inside authenticate two
inside authenticate three
inside authenticate four
inside authenticate five
*** stack smashing detected ***: ./lproxy-server terminated
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6(__fortify_fail+0x50)[0xb757f390]
/lib/tls/i686/cmov/libc.so.6(+0xe233a)[0xb757f33a]
./lproxy-server(authenticate+0x28c)[0x8049299]
./lproxy-server(main+0x49d)[0x804998e]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xb74b3bd6]
./lproxy-server[0x8048f41]
======= Memory map: ========
08048000-0804b000 r-xp 00000000 08:01 128583 /home/martin/Downloads/licproxy-server2/lproxy-server
0804b000-0804c000 r--p 00002000 08:01 128583 /home/martin/Downloads/licproxy-server2/lproxy-server
0804c000-0804d000 rw-p 00003000 08:01 128583 /home/martin/Downloads/licproxy-server2/lproxy-server
0915b000-0917c000 rw-p 00000000 00:00 0 [heap]
b6bce000-b6beb000 r-xp 00000000 08:01 2648 /lib/libgcc_s.so.1
b6beb000-b6bec000 r--p 0001c000 08:01 2648 /lib/libgcc_s.so.1
b6bec000-b6bed000 rw-p 0001d000 08:01 2648 /lib/libgcc_s.so.1
b6bfc000-b6bfd000 ---p 00000000 00:00 0
b6bfd000-b73ff000 rw-p 00000000 00:00 0
b73ff000-b7412000 r-xp 00000000 08:01 2763 /lib/libz.so.1.2.3.3
b7412000-b7413000 r--p 00012000 08:01 2763 /lib/libz.so.1.2.3.3
b7413000-b7414000 rw-p 00013000 08:01 2763 /lib/libz.so.1.2.3.3
b7414000-b7438000 r-xp 00000000 08:01 8012 /lib/tls/i686/cmov/libm-2.11.1.so
b7438000-b7439000 r--p 00023000 08:01 8012 /lib/tls/i686/cmov/libm-2.11.1.so
b7439000-b743a000 rw-p 00024000 08:01 8012 /lib/tls/i686/cmov/libm-2.11.1.so
b743a000-b744d000 r-xp 00000000 08:01 8048 /lib/tls/i686/cmov/libnsl-2.11.1.so
b744d000-b744e000 r--p 00012000 08:01 8048 /lib/tls/i686/cmov/libnsl-2.11.1.so
b744e000-b744f000 rw-p 00013000 08:01 8048 /lib/tls/i686/cmov/libnsl-2.11.1.so
b744f000-b7451000 rw-p 00000000 00:00 0
b7451000-b745a000 r-xp 00000000 08:01 7807 /lib/tls/i686/cmov/libcrypt-2.11.1.so
b745a000-b745b000 r--p 00008000 08:01 7807 /lib/tls/i686/cmov/libcrypt-2.11.1.so
b745b000-b745c000 rw-p 00009000 08:01 7807 /lib/tls/i686/cmov/libcrypt-2.11.1.so
b745c000-b7483000 rw-p 00000000 00:00 0
b7483000-b7498000 r-xp 00000000 08:01 8259 /lib/tls/i686/cmov/libpthread-2.11.1.so
b7498000-b7499000 r--p 00014000 08:01 8259 /lib/tls/i686/cmov/libpthread-2.11.1.so
b7499000-b749a000 rw-p 00015000 08:01 8259 /lib/tls/i686/cmov/libpthread-2.11.1.so
b749a000-b749d000 rw-p 00000000 00:00 0
b749d000-b75f0000 r-xp 00000000 08:01 7789 /lib/tls/i686/cmov/libc-2.11.1.so
b75f0000-b75f1000 ---p 00153000 08:01 7789 /lib/tls/i686/cmov/libc-2.11.1.so
b75f1000-b75f3000 r--p 00153000 08:01 7789 /lib/tls/i686/cmov/libc-2.11.1.so
b75f3000-b75f4000 rw-p 00155000 08:01 7789 /lib/tls/i686/cmov/libc-2.11.1.so
b75f4000-b75f7000 rw-p 00000000 00:00 0
b75f7000-b77a1000 r-xp 00000000 08:01 5403 /usr/lib/libmysqlclient.so.16.0.0
b77a1000-b77a2000 ---p 001aa000 08:01 5403 /usr/lib/libmysqlclient.so.16.0.0
b77a2000-b77a5000 r--p 001aa000 08:01 5403 /usr/lib/libmysqlclient.so.16.0.0
b77a5000-b77ea000 rw-p 001ad000 08:01 5403 /usr/lib/libmysqlclient.so.16.0.0
b77ea000-b77eb000 rw-p 00000000 00:00 0
b77ed000-b77f7000 r-xp 00000000 08:01 8119 /lib/tls/i686/cmov/libnss_files-2.11.1.so
b77f7000-b77f8000 r--p 00009000 08:01 8119 /lib/tls/i686/cmov/libnss_files-2.11.1.so
b77f8000-b77f9000 rw-p 0000a000 08:01 8119 /lib/tls/i686/cmov/libnss_files-2.11.1.so
b77f9000-b77fc000 rw-p 00000000 00:00 0
b77fc000-b77fd000 r-xp 00000000 00:00 0 [vdso]
b77fd000-b7818000 r-xp 00000000 08:01 2813 /lib/ld-2.11.1.so
b7818000-b7819000 r--p 0001a000 08:01 2813 /lib/ld-2.11.1.so
b7819000-b781a000 rw-p 0001b000 08:01 2813 /lib/ld-2.11.1.so
bfd4c000-bfd61000 rw-p 00000000 00:00 0 [stack]
Aborted
Может ли кто-нибудь пролить свет на то, что мы сделали неправильно, как мы могли бы это сделать сами и как мы могли бы избежать подобных ошибок в будущем.
Также, если у кого-то есть понимание того, почему он отлично работает в archlinux, но не в Ubuntu, мне было бы очень интересно это услышать.
2 ответа
Вы используете strcat для добавления в массив (query
), который живет в стеке и имеет только точный размер, необходимый для начального значения. Таким образом, вы переполняете буфер в стеке, что может привести ко всем видам любопытного и интересного поведения.
То, что он работает на Arch Linux, является совпадением; gcc в Ubuntu по умолчанию включает проверку стека и находит ошибку.
Эта строка выглядит подозрительно для меня:
strncpy(users[user_num].ip,inet_ntoa(cli_addr.sin_addr),strlen(inet_ntoa(cli_addr.sin_addr)));
КСТАТИ, strncpy
не добавляет трейлинг \0
, Если область, возвращаемая malloc, не очищается, это может привести к переполнению последующих строковых операций.