How to use threads with libev - c

I'm making a webcrawler and want to spawn a number of threads each with its own event loop to monitor network activity. Here is my code so far:
static void my_cb(EV_P_ struct ev_io *w, int revents)
{
GlobalInfo *g = (GlobalInfo *)w->data;
i = 0;
if (g->concurrent_connections < MAX_CONNECTIONS)
{
while (i < MAX_LOAD && i < MAX_CONNECTIONS - g->concurrent_connections)
{
add_url(g);
}
}
}
static int init(GlobalInfo *g)
{
int fd;
fd = open("myfile", O_RDWR | O_NONBLOCK, 0);
if(fd == -1) {
perror("open");
exit(1);
}
g->input = fdopen(fd, "r");
ev_io_init(&g->fifo_event, my_cb, fd, EV_READ);
ev_io_start(g->loop, &g->fifo_event);
}
void *crawler(void *threadid)
{
GlobalInfo g;
long tid;
tid = (long)threadid;
printf("Initalised thread #%ld!\n", tid);
memset(&g, 0, sizeof(GlobalInfo));
g.loop = ev_loop_new(EVFLAG_AUTO);
g.done = 0;
g.downloaded = 0;
g.head = 0;
g.added = 0;
g.concurrent_connections = 0;
init(&g);
g.multi = curl_multi_init();
ev_timer_init(&g.timer_event, timer_cb, 0., 0.);
g.timer_event.data = &g;
g.fifo_event.data = &g;
curl_multi_setopt(g.multi, CURLMOPT_SOCKETFUNCTION, sock_cb);
curl_multi_setopt(g.multi, CURLMOPT_SOCKETDATA, &g);
curl_multi_setopt(g.multi, CURLMOPT_TIMERFUNCTION, multi_timer_cb);
curl_multi_setopt(g.multi, CURLMOPT_TIMERDATA, &g);
/* we don't call any curl_multi_socket*() function yet as we have no handles
added! */
ev_loop(g.loop, 0);
curl_multi_cleanup(g.multi);
pthread_exit(NULL);
}
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
mysql_start();
pthread_t threads[NUM_THREADS];
int rc;
long t;
for(t=0; t<NUM_THREADS; t++){
rc = pthread_create(&threads[t], NULL, crawler, (void *)t);
if (rc){
printf("ERROR; return code from pthread_create() is %d\n", rc);
exit(-1);
}
}
pthread_exit(NULL);
mysql_stop();
mysql_library_end();
return 0;
}
As you can see I attempt to make a new event loop in each thread with ev_loop_new. However, the program aborts with the following backtrace:
#0 __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50
#1 0x00007ffff75bb859 in __GI_abort () at abort.c:79
#2 0x00007ffff76263ee in __libc_message (action=action#entry=do_abort, fmt=fmt#entry=0x7ffff7750285 "%s\n") at ../sysdeps/posix/libc_fatal.c:155
#3 0x00007ffff762e47c in malloc_printerr (str=str#entry=0x7ffff7752690 "double free or corruption (!prev)") at malloc.c:5347
#4 0x00007ffff763012c in _int_free (av=0x7ffff7781b80 <main_arena>, p=0x5555569ff1e0, have_lock=<optimized out>) at malloc.c:4317
#5 0x00007ffff79518c4 in ?? () from /lib/x86_64-linux-gnu/libmysqlclient.so.21
#6 0x00007ffff795215a in ?? () from /lib/x86_64-linux-gnu/libmysqlclient.so.21
#7 0x00007ffff78ffd0e in ?? () from /lib/x86_64-linux-gnu/libmysqlclient.so.21
#8 0x00007ffff78fffa5 in ?? () from /lib/x86_64-linux-gnu/libmysqlclient.so.21
#9 0x00007ffff7900155 in ?? () from /lib/x86_64-linux-gnu/libmysqlclient.so.21
#10 0x00007ffff7903795 in ?? () from /lib/x86_64-linux-gnu/libmysqlclient.so.21
#11 0x00007ffff7905804 in mysql_real_query_nonblocking () from /lib/x86_64-linux-gnu/libmysqlclient.so.21
#12 0x0000555555559a4e in add_url (g=0x7ffff6e71e50) at threads.c:1202
#13 0x000055555555a69b in my_cb (loop=0x7ffff0000f70, w=0x7ffff6e71e58, revents=1) at threads.c:1457
#14 0x00007ffff7810bc3 in ev_invoke_pending () from /lib/x86_64-linux-gnu/libev.so.4
#15 0x00007ffff7814b93 in ev_run () from /lib/x86_64-linux-gnu/libev.so.4
#16 0x0000555555556a73 in ev_loop (loop=0x7ffff0000f70, flags=0) at /usr/include/ev.h:842
#17 0x000055555555a990 in crawler (threadid=0x0) at threads.c:1517
#18 0x00007ffff78b1609 in start_thread (arg=<optimized out>) at pthread_create.c:477
#19 0x00007ffff76b8293 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
(gdb) q
As you can see the offending line is ev_loop(g.loop, 0); which causes the abort. I thought I was doing the right thing with making a new event loop in each thread with ev_loop_new.
What am I doing wrong?

Related

web server returns segfault when reloading webpage constantly + other error

I'm making a C web server on my raspberry pi and I've come across a problem where when I constantly reload the webpage, the web server gives me a segmentation fault. It also sometimes refuses to run the threads after a while of constant traffic. I used gdb to debug the segfault and this is what I found:
Thread 145 "webServer" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0x6f59b440 (LWP 21885)]
memcpy () at ../sysdeps/arm/memcpy.S:196
196 ../sysdeps/arm/memcpy.S: No such file or directory.
(gdb) where
#0 memcpy () at ../sysdeps/arm/memcpy.S:196
#1 0xb6e94b48 in __GI__IO_file_xsgetn (fp=0x52719f08, data=<optimized out>,
n=1) at fileops.c:1303
#2 0xb6e87df8 in __GI__IO_fread (buf=0x2343c <fileLine>, size=1, count=1,
fp=0x52719f08) at iofread.c:38
#3 0x000114c4 in handleClient (pClientSock=0x23830 <clientSock>)
at webServer.c:242
#4 0x000116a0 in giveThreadWork () at webServer.c:298
#5 0xb6f7f494 in start_thread (arg=0x6f59b440) at pthread_create.c:486
#6 0xb6f02568 in ?? () at ../sysdeps/unix/sysv/linux/arm/clone.S:73
from /lib/arm-linux-gnueabihf/libc.so.6
Backtrace stopped: previous frame identical to this frame (corrupt stack?)
I couldn't find anything for the refusing threads though.
It says that there are some problems on lines: 242 and 298. All there is there, is the following:
Line 242-266
============
while ((freadErr = fread(fileLine, sizeof(fileLine), 1, fpointer)) != 0) {
if (freadErr < 0) {
printf("fread error\n");
perror("fread");
close(acceptSock);
memset_all();
return NULL;
}
if ((send(acceptSock, httpResponse, strlen(httpResponse), MSG_NOSIGNAL)) == -1) {
printf("write error1\n");
perror("we1");
close(acceptSock);
memset_all();
return NULL;
}
if ((send(acceptSock, fileLine, sizeof(fileLine), MSG_NOSIGNAL)) == -1) {
printf("write error2\n");
perror("we2");
close(acceptSock);
memset_all();
return NULL;
}
memset(fileLine, 0, 1);
memset(httpResponse, 0, 1000);
line 285-304 (298 is where error is)
==================
void *giveThreadWork() {
while (1) {
int *pclient;
pthread_mutex_lock(&mutex);
if ((pclient = dequeue()) == NULL) {
pthread_cond_wait(&condition_var, &mutex);
pclient = dequeue();
}
pthread_mutex_unlock(&mutex);
if (pclient != NULL) {
handleClient(pclient);
} else {
printf("\n\n\n\n\n no more listener \n\n\n\n\n");
close(*pclient);
}
}
}
I can't get anymore info from gdb but maybe someone else knows how. I've tried changing the strcpy() functions and checked all the string manipulation functions (or i'm pretty sure I have) and I've found nothing.
Here is all the code if anyone needs it: https://www.toptal.com/developers/hastebin/xokipuvidu.c
Hopefully someone can help or point me in the right direction

double free or corruption (!prev) pthread

I'm having issues while implementing a multithread program.
The program seems to work fine for a single thread (when I set THREADS to 1) but for NTHREADS > 1, I'm getting the following error:
Segmentation fault (core dumped)
or
double free or corruption (!prev)
or
free(): invalid size: 0xb6b00a10 ***
0Aborted (core dumped)
as you can see the error varies a lot and I'm getting confused.
The program I'm executing is the following:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <pthread.h>
#define NTHREADS 5
typedef struct data_t
{
int num;
FILE *fp;
pthread_mutex_t mutex;
int thread_id;
}data_t;
void writefp(int num1, FILE *fp){
if(fp!=NULL){
int i;
int nume = 1;
int long_var=log10(nume);
for(i=long_var;i>=0;i--){
nume=(num1 / (int) round(pow(10, i)) % 10);
char d=nume+'0';
fwrite(&d, 1, 1, fp);
printf("%c", d);
}
}
fclose(fp);
}
void *thread_writefp(void* args)
{
data_t *data = (data_t *)args;
printf(" Thread id %d\n", data->thread_id);
pthread_mutex_lock(&(data->mutex));
writefp(data->num, data->fp);
pthread_mutex_unlock(&(data->mutex));
pthread_exit(NULL);
}
int randomf(){
int num,i;
for(i = 0; i<2; i++) {
num = rand()%100000+1;
}
return num;
}
int prime(int num1){
int is_prime=1;
int i = 2;
printf("Number: ");
while( i<=num1/2 && is_prime==1 ) {
printf("%i ", i);
if(i%30==0){
printf("\n");
}
if( num1 % i == 0 ) {
is_prime = 0;
}
i++;
}
printf("\n");
if(is_prime){
printf("%i is number prime\n", num1);
}else{
printf("NO is prime %i\n",num1);
}
return 0;
}
int main(void){
int i;
//int num1=randomf();
srand(time(NULL));
FILE *fp = fopen("fich.txt", "w+b");
data_t data;
pthread_t consumers_thread[NTHREADS];
data.mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
data.fp = fp;
//writefp( num1, fp);
for(i = 0; i < NTHREADS; i++)
{
data.num = randomf();
data.thread_id = i;
printf("Number prime is %i\n", prime(data.num));
if(pthread_create(&consumers_thread[i], NULL,
thread_writefp, (void*) &data) != 0)
{
fprintf(stderr, "%s\n", "Error creating thread!");
return EXIT_FAILURE;
}
}
// wait for all consumers thread to finish
for(i = 0; i < NTHREADS; ++i)
{
pthread_join(consumers_thread[i], NULL);
}
return EXIT_SUCCESS;
}
I compile the program as follows :
$gcc -pthread -Wall -o consummer consummer.c -lm
Here are for exemple tree error I got when I ran it with gdb tree successive time without changing anything to the code:
1
Thread 2 "consummer" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7cc1b40 (LWP 18122)]
tcache_thread_freeres () at malloc.c:3003
3003 malloc.c: No such file or directory.
(gdb) bt
#0 tcache_thread_freeres () at malloc.c:3003
#1 0xb7e258c2 in __libc_thread_freeres () at thread-freeres.c:29
#2 0xb7ea03ad in start_thread (arg=0xb7cc1b40) at pthread_create.c:478
#3 0xb7dbb0a6 in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:108
(gdb)
2
Thread 3 "consummer" received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb72ffb40 (LWP 18131)]
0xb7d2af2b in __GI__IO_fwrite (buf=0xb72ff30f, size=1, count=1, fp=0x404160) at iofwrite.c:37
37 iofwrite.c: No such file or directory.
(gdb) run
3
Thread 3 "consummer" received signal SIGABRT, Aborted.
[Switching to Thread 0xb74c0b40 (LWP 18143)]
0xb7fd7cf9 in __kernel_vsyscall ()
(gdb) bt
#0 0xb7fd7cf9 in __kernel_vsyscall ()
#1 0xb7cf17e2 in __libc_signal_restore_set (set=0xb74bfe9c) at ../sysdeps/unix/sysv/linux/nptl-signals.h:80
#2 __GI_raise (sig=6) at ../sysdeps/unix/sysv/linux/raise.c:48
#3 0xb7cf2f51 in __GI_abort () at abort.c:90
#4 0xb7d340cc in __libc_message (action=(do_abort | do_backtrace), fmt=<optimized out>) at ../sysdeps/posix/libc_fatal.c:181
#5 0xb7d3af5d in malloc_printerr (action=<optimized out>, str=0xb7e418d8 "double free or corruption (!prev)", ptr=<optimized out>,
ar_ptr=0xb7e967a0 <main_arena>) at malloc.c:5425
#6 0xb7d3bb3b in _int_free (av=0xb7e967a0 <main_arena>, p=<optimized out>, have_lock=have_lock#entry=0) at malloc.c:4174
#7 0xb7d3fcb0 in __GI___libc_free (mem=0x404160) at malloc.c:3144
#8 0xb7e2587d in tcache_thread_freeres () at malloc.c:3004
#9 0xb7e258c2 in __libc_thread_freeres () at thread-freeres.c:29
#10 0xb7ea03ad in start_thread (arg=0xb74c0b40) at pthread_create.c:478
#11 0xb7dbb0a6 in clone () at ../sysdeps/unix/sysv/linux/i386/clone.S:108
(gdb)
I'd like/apreciate your help to know what I did wrong please. Thanks in advance.
Per this answer (but see Edit 2), multiple threads cannot safely access the same FILE *fp. As #IlyaBursov pointed out, you only have one data_t data shared across all threads — and, therefore, only one FILE * data.fp.
Thanks for your comment noting that you moved the fopen into the thread function. That way each thread independently opens and closes the file, so there is no FILE * sharing between threads.
This seems to be implementation-dependent — I was not able to reproduce the issue on Cygwin x64 with gcc 6.4.0. I suspect the effect of the mutex may also vary by implementation. It may also be dependent on compiler options — see this example.
Edit As #MichaelDorgan pointed out, calling fclose on a FILE * that other threads are using is also a bad idea.
Edit 2 As #JohnBollinger points out, individual stream operations are thread-safe these days. That would suggest that the fclose before another thread tried to access the file might be the problem. However, I wonder if perhaps the OP's stdio implementation is non-conformant in some way. I would think a compliant fwrite would simply return error on an access to a closed file, rather than crashing. See further comments below.

SIGABRT in C Linux

i received an strange SIGABRT in my C program, tha's my function where the problem appears:
int get_interface_mac_addr(Interface* iface) {
char arquivo[10];
sprintf(arquivo, "/sys/class/net/%s/address", iface->interface_name);
int fd;
fd = open(arquivo, O_RDONLY, 0);
char buf[100];
read(fd, buf, sizeof (buf));
buf[strlen(buf) - 1] = '\0';
strcpy(iface->interface_mac_addr, buf);
close(fd);
return GET_MAC_ADDR_SUCCESS;
}
The error happends at "}", the last line of code.
I try to debug with GDB, but I'm new at this, so I do not understand many things that GDB tells me. Below is the output from GDB:
Core was generated by `./vfirewall-monitor'.
Program terminated with signal 6, Aborted.
#0 0x00007f36c043b425 in __GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
64 ../nptl/sysdeps/unix/sysv/linux/raise.c: No such file or directory.
(gdb) bt
#0 0x00007f36c043b425 in __GI_raise (sig=<optimized out>) at ../nptl/sysdeps/unix/sysv/linux/raise.c:64
#1 0x00007f36c043eb8b in __GI_abort () at abort.c:91
#2 0x00007f36c047939e in __libc_message (do_abort=2, fmt=0x7f36c058157f "*** %s ***: %s terminated\n")
at ../sysdeps/unix/sysv/linux/libc_fatal.c:201
#3 0x00007f36c050ff47 in __GI___fortify_fail (msg=0x7f36c0581567 "stack smashing detected") at fortify_fail.c:32
#4 0x00007f36c050ff10 in __stack_chk_fail () at stack_chk_fail.c:29
#5 0x00000000004029be in get_interface_mac_addr (iface=0x7f36b4004560) at interfaces.c:340
#6 0x00000000004022c9 in get_interfaces_info (iface=0x7f36b4004560) at interfaces.c:87
#7 0x0000000000402d9d in get_all_system_info () at kernel.c:109
#8 0x00007f36c07cce9a in start_thread (arg=0x7f36bb729700) at pthread_create.c:308
#9 0x00007f36c04f93fd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
#10 0x0000000000000000 in ?? ()
(gdb)
Someone know whats going on in this case? I do something wrong and a can see what is?
many thanks.
char arquivo[10]; // <-- here
sprintf(arquivo, "/sys/class/net/%s/address", iface->interface_name);
arquivo is way too small for that string.
You should also check the return value of open():
fd = open(arquivo, O_RDONLY, 0);
if(fd < 0) {
perror("open");
// do something
}
This also wrong:
read(fd, buf, sizeof (buf));
buf[strlen(buf) - 1] = '\0';
^^^^^^^^^^^
read() does not null terminate anything. You can't call strlen() on buf. Instead:
int n = read(fd, buf, sizeof (buf));
if(n < 0) {
perror("read");
// do something
}
buf[n] = '\0';

Sqlite3_bind_text segmentation fault

My app does a recursive scan of a given directory, I store each unique path in a database.
At one point I get a segfault and glibc fires:
*** glibc detected *** ./test: double free or corruption (!prev): 0x08cd1a20 ***
gdb confirms it.
The problem occurs when calling the function below:
int
populatePathDB(sqlite3* db, char *absolutePath)
{
char *sql;
sqlite3_stmt *stmt ;
int ret;
sql = "INSERT INTO paths (path) VALUES (?)";
ret = sqlite3_prepare_v2(db,sql,-1,&stmt,NULL);
if ( ret != SQLITE_OK)
DB_ERR(db, sqlite3_errmsg(db));
ret = sqlite3_bind_text(stmt, 1, absolutePath, -1, SQLITE_STATIC);
if ( ret != SQLITE_OK)
DB_ERR(db, sqlite3_errmsg(db));
sqlite3_step(stmt);
if ( ret != SQLITE_OK)
DB_ERR(db, sqlite3_errmsg(db));
/* sqlite3_clear_bindings(stmt);
if ( ret != SQLITE_OK)
DB_ERR(db, sqlite3_errmsg(db));
sqlite3_reset(stmt);
if ( ret != SQLITE_OK)
DB_ERR(db, sqlite3_errmsg(db)); */
ret = sqlite3_finalize( stmt );
if ( ret != SQLITE_OK)
DB_ERR(db, sqlite3_errmsg(db));
return SQLITE_OK;
}
I guess the variable absolutePath (allocated by the caller) is deleted twice. Even Using SQLITE_TRANSIENT i've got the same fault.
Any ideas?
Adding recursive scan function:
int walkDir( char *dir, unsigned int recursive)
{
DIR* dirstream;
struct stat statbuf;
struct dirent *entry = NULL;
int exists, fd;
size_t dlen, entlen, nlen;
size_t buflen = 0;
char *baseName = NULL;
extern sqlite3 *magicDB_g;
extern sqlite3 *pathDB_g;
assert (dir !=NULL);
if ( ( dirstream = opendir(dir) ) == NULL ) {
ERR_MSG("opendir");
return (EXIT_FAILURE);
}
dlen = strlen(dir);
buflen = MEMCHUNK;
if (dlen >= buflen)
buflen = roundToNextPowerOf2(dlen);
baseName = xmalloc(buflen);
while ( ( entry = readdir(dirstream) ) ) {
if(!strcmp(".",entry->d_name) ||
! strcmp("..",entry->d_name))
continue;
if (entry->d_name[0] == '.')
continue;
nlen = dlen + (entlen = strlen(entry->d_name));
if (unlikely(nlen + 2 > buflen) )
xrealloc(baseName, buflen << 1);
if (dlen == 1 && *dir == '/' )
sprintf(baseName, "%s%s" , dir, entry->d_name);
else
sprintf(baseName, "%s/%s" , dir, entry->d_name);
/*snprintf (baseName, need + 2,"%s/%s", baseName, entry->d_name);*/
exists = lstat(baseName, &statbuf);
if (exists < 0)
continue;
if ( S_ISREG(statbuf.st_mode) && statbuf.st_size != 0 )
{
if ((fd = open(baseName, O_RDONLY)) == -1) {
ERR_MSG("open");
continue;
}
//fileSignature_v1(fd,NBYTES,magicDB_g);
if ( close(fd) == -1)
ERR_MSG("close");
}
if ( S_ISDIR(statbuf.st_mode) )
{
/* Create a absolute path database with unique entries */
populatePathDB(pathDB_g, baseName); <-- No segfault if not called.
if (recursive) {
printf("basename: %s\n",baseName);
walkDir(baseName,recursive);
}
}
}
free(baseName); <-- Seems to be deleted twice when back from populatePathDB()
if (closedir(dirstream) == -1)
ERR_MSG("closedir");
return (EXIT_SUCCESS);
}
Here's the error statement:
Program received signal SIGSEGV, Segmentation fault.
_int_malloc (av=0x379440, bytes=34) at malloc.c:3598
3598 malloc.c: Aucun fichier ou dossier de ce type.
(gdb) bt
#0 _int_malloc (av=0x379440, bytes=34) at malloc.c:3598
#1 0x0024fd3c in __GI___libc_malloc (bytes=34) at malloc.c:2924
#2 0x0011541f in local_strdup (s=0xb7fe2a8c "/lib/i386-linux-gnu/libgcc_s.so.1") at dl-load.c:162
#3 0x001185d4 in _dl_map_object (loader=<optimized out>, name=<optimized out>, type=2, trace_mode=0, mode=-1879048191, nsid=0) at dl-load.c:2473
#4 0x00122d5d in dl_open_worker (a=0xbfffe690) at dl-open.c:225
#5 0x0011ecbf in _dl_catch_error (objname=0xbfffe6b4, errstring=0xbfffe6b8, mallocedp=0xbfffe6bf, operate=0x122c30 <dl_open_worker>, args=0xbfffe690)
at dl-error.c:178
#6 0x001227e4 in _dl_open (file=0x334345 "libgcc_s.so.1", mode=-2147483647, caller_dlopen=0x2d7e38, nsid=-2, argc=2, argv=0xbffff314, env=0x8051040)
at dl-open.c:639
#7 0x002fbd41 in do_dlopen (ptr=0xbfffe840) at dl-libc.c:89
#8 0x0011ecbf in _dl_catch_error (objname=0xbfffe814, errstring=0xbfffe818, mallocedp=0xbfffe81f, operate=0x2fbce0 <do_dlopen>, args=0xbfffe840)
at dl-error.c:178
#9 0x002fbe37 in dlerror_run (operate=<optimized out>, args=<optimized out>) at dl-libc.c:48
#10 0x002fbec7 in __GI___libc_dlopen_mode (name=0x334345 "libgcc_s.so.1", mode=-2147483647) at dl-libc.c:165
#11 0x002d7e38 in init () at ../sysdeps/i386/backtrace.c:44
#12 0x00388e8e in pthread_once () at ../nptl/sysdeps/unix/sysv/linux/i386/pthread_once.S:122
#13 0x002d80a5 in __GI___backtrace (array=0xbfffee90, size=64) at ../sysdeps/i386/backtrace.c:121
#14 0x00241310 in __libc_message (do_abort=2, fmt=0x3393bc "*** glibc detected *** %s: %s: 0x%s ***\n") at ../sysdeps/unix/sysv/linux/libc_fatal.c:180
#15 0x0024be42 in malloc_printerr (action=<optimized out>, str=<optimized out>, ptr=0x80c4eb8) at malloc.c:5007
#16 0x0804aa8f in walkDir (dir=0x8075a60 "/home/olivier/Téléchargements", recursive=1) at dirtraverser.c:251
#17 0x0804aa63 in walkDir (dir=0x80528f8 "/home/olivier", recursive=1) at dirtraverser.c:245
#18 0x0804bdc0 in main (argc=2, argv=0xbffff314) at main.c:246
Your xrealloc call (xrealloc(baseName, buflen << 1);) is wrong: you aren't assigning its result to baseName. xrealloc may free the buffer you give it (and return a different pointer), so if you don't assign the result then you may double-free the buffer.

Segfault in multithreaded download program

I am trying to write a simple program. It's supposed to read links from stdin, and download those links in seperate threads. I wrote the following code, but I am getting segmetation fault. Can anyone guess why?
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* memcpy */
#include <curl/curl.h>
#include <pthread.h>
#define NUMTHREADS 3
struct downloadfile {
char *filename;
FILE *stream;
};
pthread_mutex_t mutex;
/* writedata: custom fwrite for curl writefunction */
static size_t writedata(void *buffer, size_t size, size_t nmemb, void *stream)
{
struct downloadfile *out = (struct downloadfile *) stream;
if (out && !out->stream) {
out->stream = fopen(out->filename, "w");
if (!out->stream)
return -1; /* can't open file to write */
}
return fwrite(buffer, size, nmemb, out->stream);
}
/* getfilename: gets a file's name from a link. */
char *getfilename(const char *link)
{
const char *fnstart = NULL; /* start of filename*/
size_t len = 0; /* length of filename*/
for ( ; *link != '\0'; ++link) {
if (*link == '/') {
fnstart = link + 1;
len = 0;
} else {
++len;
}
}
char *filename = malloc(len + 1);
memcpy(filename, fnstart, len);
filename[len] = '\0';
return filename;
}
/* downloadthread: get a line from stdin, and try to donwload it.*/
void *downloadthread(void *ignored)
{
puts("in a download thread");
CURL *curl;
curl = curl_easy_init();
ssize_t read; /* number of characters read from a line */
if (!curl) { /* couldn't get curl handle */
fputs("Couldn't get curl handle", stderr);
pthread_exit(NULL);
}
for (;;) { /* readline and download loop */
size_t n; /* argument to getline */
char *lineptr = NULL; /* argument to getline */
struct downloadfile ofile;
/* I think I need mutex protect the getline, but I am not sure */
pthread_mutex_lock(&mutex);
read = getline(&lineptr, &n, stdin);
pthread_mutex_unlock(&mutex);
if (read == EOF)
break;
ofile.filename = getfilename(lineptr);
curl_easy_setopt(curl, CURLOPT_URL,lineptr);
/* follow http redirects */
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION ,1L);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writedata);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ofile);
curl_easy_perform(curl);
free(ofile.filename);
free(lineptr);
if (ofile.stream)
fclose(ofile.stream);
}
curl_easy_cleanup(curl);
pthread_exit(NULL);
}
int main()
{
size_t i;
int rc;
pthread_t threads[NUMTHREADS];
curl_global_init(CURL_GLOBAL_ALL);
pthread_mutex_init(&mutex, NULL);
/* fire up threads */
for (i = 0; i < NUMTHREADS; i++) {
rc = pthread_create(&threads[i], NULL, downloadthread, NULL);
if (rc) {
printf("Error, return code from pthread is %d\n", rc);
exit(-1);
}
}
/* join all threads before cleaning up */
for (i = 0; i < NUMTHREADS; i++)
pthread_join(threads[i], NULL);
/* cleanup and exit */
pthread_mutex_destroy(&mutex);
pthread_exit(NULL);
}
Edit: Here is the output of the gdb. It didn't give much idea to me.
[New Thread 0xb61feb40 (LWP 3778)]
[New Thread 0xb57ffb40 (LWP 3779)]
[New Thread 0xb4ffeb40 (LWP 3780)]
[Thread 0xb61feb40 (LWP 3778) exited]
[Thread 0xb57ffb40 (LWP 3779) exited]
[Thread 0xb4ffeb40 (LWP 3780) exited]
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread 0xb7b25b40 (LWP 3773)]
0xb7e02310 in fwrite () from /lib/libc.so.6
(gdb) bt
#0 0xb7e02310 in fwrite () from /lib/libc.so.6
#1 0xb7f6dd53 in ?? () from /usr/lib/libcurl.so.4
#2 0xb7f85a5e in ?? () from /usr/lib/libcurl.so.4
#3 0xb7f86bb5 in ?? () from /usr/lib/libcurl.so.4
#4 0xb7f87573 in curl_easy_perform () from /usr/lib/libcurl.so.4
#5 0x08048d99 in downloadthread (ignored=0x0) at downloader.c:91
#6 0xb7f47ce8 in start_thread () from /lib/libpthread.so.0
#7 0xb7e874de in clone () from /lib/libc.so.6
When you declare struct downloadfile ofile, its stream field is filled with garbage and probably not 0. When ofile is then passed to writedata callback (as a result of calling curl_easy_perform), the condition out && !out->stream can thus be false and cause writedata call fwrite on unopened stream.
So just replace ofile declaration with struct downloadfile ofile = { 0, 0 };.
for ( ; *link != '\0'; ++link) {
In case the path does not contain a '/' :
for (fnstart=link ; *link != '\0'; ++link) {
of (below the loop) if (!fnstart) return BAD_STUFF;
Check the char *getfilename(const char *link) function. If the character array passed as a parameter does not contain any /, the const char *fnstart variable will stay to be NULL, you'll ultimately try to memcpy at least one byte from NULL.

Resources