Sqlite3_bind_text segmentation fault - c

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.

Related

How to use threads with libev

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?

libhiredis does not work when passing redisContext into another function

I am trying to write my own wrapper function to libhiredis to use in my project but when i try to pass the redis_context into another function to issue commands from there. The code segfaults and reports this through gdb:
GDB Error:
Program received signal SIGSEGV, Segmentation fault.
sdscatlen (s=0x0, t=0x555555757690, len=22) at sds.c:239
239 sds.c: No such file or directory.
(gdb) backtrace
#0 sdscatlen (s=0x0, t=0x555555757690, len=22) at sds.c:239
#1 0x00007ffff7bcd300 in __redisAppendCommand (c=0x7fffffffddd0, cmd=<optimized out>, len=<optimized out>) at hiredis.c:910
#2 0x00007ffff7bcd38c in redisvAppendCommand (c=0x7fffffffddd0, format=<optimized out>, ap=<optimized out>) at hiredis.c:942
#3 0x00007ffff7bcd579 in redisvCommand (c=0x7fffffffddd0, format=<optimized out>, ap=ap#entry=0x7fffffffdcc0) at hiredis.c:1003
#4 0x00007ffff7bcd634 in redisCommand (c=<optimized out>, format=<optimized out>) at hiredis.c:1012
#5 0x0000555555554b9e in getnow (redis_context=0x7fffffffddd0) at src/testRedis.c:18
#6 0x0000555555554c14 in main () at src/testRedis.c:49
Here is the code:
RedisWrapper.h:
#ifndef REDIS_WRAPPER_H
#define REDIS_WRAPPER_H
int redis_wrapper_init(redisContext *redis_context, char *ip, int port);
int redis_wrapper_set(redisContext *redis_context, char *key, char *value);
int redis_wrapper_get(redisContext *redis_context, char *key, char *retrieved_value);
#endif
RedisWrapper.c:
#include <hiredis.h>
int redis_wrapper_init(redisContext *redis_context, char* ip, int port) {
redis_context = redisConnect(ip, port);
if (redis_context == NULL || redis_context->err) {
if (redis_context) {
fprintf(stderr, "cget: redis init error: %s\n", redis_context->errstr);
} else {
fprintf(stderr, "cget: can't allocate redis context\n");
}
return 1;
}
return 0;
}
int redis_wrapper_set(redisContext *redis_context, char *key, char *value) {
redisReply *reply = redisCommand(redis_context, "SET %s %s", key, value);
if(reply == NULL) {
fprintf(stderr, "cget: redis set error key: %s, val: %s\n", key, value);
fprintf(stderr, "cget: redis set error: %s\n", redis_context->errstr);
return 1;
}
freeReplyObject(reply);
return 0;
}
int redis_wrapper_get(redisContext *redis_context, char *key, char *retrieved_value) {
redisReply *reply = redisCommand(redis_context, "GET foo");
if(reply == NULL) {
fprintf(stderr, "cget: redis get error key: %s\n", key);
fprintf(stderr, "cget: redis get error: %s\n", redis_context->errstr);
return 1;
}
printf("GET: %s\n", reply->str);
retrieved_value = reply->str;
freeReplyObject(reply);
return 0;
}
Main.c
#include <stdio.h>
#include <string.h>
#include <hiredis.h>
#include "RedisWrapper.h"
void getnow(redisContext *redis_context) {
redisReply *reply = redisCommand(redis_context, "GET foo");
printf("GET foo: %s\n", reply->str);
freeReplyObject(reply);
}
int main() {
redisContext *redis_context;
redis_wrapper_init(redis_context, "127.0.0.1", 6379);
getnow(redis_context);
return 0;
}
My compile command:
gcc -Wall -g -o src/redisTest src/RedisWrapper.c `pkg-config --cflags hiredis` src/Main.c `pkg-config --libs hiredis`
More Details:
# uname -a
Linux node1 4.13.0-21-generic #24-Ubuntu SMP Mon Dec 17 17:29:16 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
# locate libhiredis
/usr/lib/x86_64-linux-gnu/libhiredis.a
/usr/lib/x86_64-linux-gnu/libhiredis.so
/usr/lib/x86_64-linux-gnu/libhiredis.so.0.13
/usr/share/doc/libhiredis-dbg
/usr/share/doc/libhiredis-dev
/usr/share/doc/libhiredis0.13
/usr/share/doc/libhiredis-dbg/changelog.Debian.gz
/usr/share/doc/libhiredis-dbg/copyright
/usr/share/doc/libhiredis-dev/README.md.gz
/usr/share/doc/libhiredis-dev/changelog.Debian.gz
/usr/share/doc/libhiredis-dev/copyright
/usr/share/doc/libhiredis0.13/changelog.Debian.gz
/usr/share/doc/libhiredis0.13/copyright
/var/cache/apt/archives/libhiredis-dbg_0.13.3-2_amd64.deb
/var/cache/apt/archives/libhiredis0.13_0.13.3-2_amd64.deb
/var/lib/dpkg/info/libhiredis-dbg:amd64.list
/var/lib/dpkg/info/libhiredis-dbg:amd64.md5sums
/var/lib/dpkg/info/libhiredis-dev:amd64.list
/var/lib/dpkg/info/libhiredis-dev:amd64.md5sums
/var/lib/dpkg/info/libhiredis0.13:amd64.list
/var/lib/dpkg/info/libhiredis0.13:amd64.md5sums
/var/lib/dpkg/info/libhiredis0.13:amd64.shlibs
/var/lib/dpkg/info/libhiredis0.13:amd64.symbols
/var/lib/dpkg/info/libhiredis0.13:amd64.triggers
What i found strange is that if i do all the operations all in the Main.c and not use the wrapper files it works. That is not useful to my project though, i need to be able to pass the redis_context around and have it work.
redis_context in redis_wrapper_init should be passed by reference (in C by pointer) to keep result of assignment redis_context = redisConnect(ip, port); after calling this function. Without this, you assign pointer returned by redisConnect to local variable, which is destroyed when function returns.
So definition should be
int redis_wrapper_init(redisContext ** redis_context, char* ip, int port) {
*redis_context = redisConnect(ip, port);
if (*redis_context == NULL || (*redis_context)->err) {
if (*redis_context) {
fprintf(stderr, "cget: redis init error: %s\n", (*redis_context)->errstr);
}
...
}
}
in main you get address of redis_context variable and pass it to redis_wrapper_init
redisContext *redis_context = 0;
redis_wrapper_init(&redis_context, "127.0.0.1", 6379);

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';

libjpeg jpeg_stdio_dest() seg fault

Hello I have a little problem with writting data to jpeg:
Here is the code snippet:
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
/* More stuff */
FILE * outfile; /* target file */
JSAMPROW row_pointer[1]; /* pointer to JSAMPLE row[s] */
int row_stride; /* physical row width in image buffer */
cinfo.err = jpeg_std_error(&jerr);
jpeg_create_compress(&cinfo);
if ((outfile = fopen(filename, "wb+")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
else
printf("File successfully opened\n");
jpeg_stdio_dest(&cinfo, outfile);
cinfo.image_width = img_w;
cinfo.image_height = img_h;
cinfo.input_components = 3;
cinfo.in_color_space = JCS_RGB;
It gives me a segfault on jpeg_stdio_dest, gdb output:
#0 0x00007ffff7855735 in malloc_consolidate () from /usr/lib/libc.so.6
#1 0x00007ffff7856a08 in _int_malloc () from /usr/lib/libc.so.6
#2 0x00007ffff7858cc0 in malloc () from /usr/lib/libc.so.6
#3 0x00007ffff7bb4b17 in ?? () from /usr/lib/libjpeg.so.8
#4 0x00007ffff7b9f225 in jpeg_stdio_dest () from /usr/lib/libjpeg.so.8
#5 0x0000000000400f2e in write_jpeg_file (
image_buffer=0x608070 "\002\002\002\002\002\002\366\366\366\365\365\365\001\001\001\003 \003\003\371\371\371\367\367\367\362\362\362\361\361\361\002\002\002\003\003\003\373\373\373\373\373\373\001\001\001\002\002\002", img_h=4, img_w=4, filename=0x7fffffffe6d1 "lol.jpg", quality=100) at jpegapi.c:39
#6 0x0000000000400e1f in main (argc=5, argv=0x7fffffffe2e8) at main.c:28
I have no idea where to search, any help is appreciated
The file opening can fail and yet you do a jpeg_stdio_dest call
bracket problem?
if ((outfile = fopen(filename, "wb+")) == NULL) {
fprintf(stderr, "can't open %s\n", filename);
exit(1);
}
else
{
printf("File successfully opened\n");
jpeg_stdio_dest(&cinfo, outfile);
}

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