From: graham.gower Date: Thu, 19 Nov 2009 22:40:55 +0000 (+0000) Subject: Fix hang in waitpid, exposed by r310. Patch from Enrico Scholz X-Git-Url: https://git.librecmc.org/?a=commitdiff_plain;h=b6c404659768c0be9d4d7b93a8d475c76ee26380;p=oweals%2Fopkg-lede.git Fix hang in waitpid, exposed by r310. Patch from Enrico Scholz , his analysis follows. libbb/unarchive.c: prevent opkg hang when subprocess is stuck in a write() call on a filled pipe and main process (which assumes that end of data from pipe has been reached) waits for this died subprocess. This patch swaps the original wait(pid) + close(pipe) sequencse so that pipe is closed first. The 'ar' code path has been fixed too by breaking the loop when requested data have been found. Previously, the loop continued at the (wrongly calculated) next position in the stream. The patch moves the stream cleanup at a better place. Variable declarations were moved to inner scopes too to ease detection of broken deallocation. NOTE: the | f = fdopen(...); | while (... /* do fread(f) */ ...) { /* ==1== */ | /* this is done in gz_open() */ | pid = fork(); | if (pid == 0) { | fread(f); /* ==2== */ | _exit(0); | } | } code looks problematic because '==2==' might update f's internal buffer. As ==2== is done in an own process, these changes are not seen by ==1==. It works only because gz_open() is called not more than one time and the loops break (after the patch). git-svn-id: http://opkg.googlecode.com/svn/trunk@340 e8e0d7a0-c8d9-11dd-a880-a1081c7ac358 --- diff --git a/libbb/unarchive.c b/libbb/unarchive.c index 14004d3..5e9c13a 100644 --- a/libbb/unarchive.c +++ b/libbb/unarchive.c @@ -612,13 +612,11 @@ char *deb_extract(const char *package_filename, FILE *out_stream, const int extract_function, const char *prefix, const char *filename) { FILE *deb_stream = NULL; - FILE *uncompressed_stream = NULL; file_header_t *ar_header = NULL; const char **file_list = NULL; char *output_buffer = NULL; char *ared_file = NULL; char ar_magic[8]; - int gunzip_pid = 0; if (filename != NULL) { file_list = xmalloc(sizeof(char *) * 2); @@ -652,6 +650,8 @@ char *deb_extract(const char *package_filename, FILE *out_stream, while ((ar_header = get_header_ar(deb_stream)) != NULL) { if (strcmp(ared_file, ar_header->name) == 0) { + int gunzip_pid = 0; + FILE *uncompressed_stream; /* open a stream of decompressed data */ uncompressed_stream = gz_open(deb_stream, &gunzip_pid); if (uncompressed_stream == NULL) { @@ -660,13 +660,14 @@ char *deb_extract(const char *package_filename, FILE *out_stream, archive_offset = 0; output_buffer = unarchive(uncompressed_stream, out_stream, get_header_tar, free_header_tar, extract_function, prefix, file_list); + fclose(uncompressed_stream); + gz_close(gunzip_pid); + break; } seek_sub_file(deb_stream, ar_header->size); free (ar_header->name); free (ar_header); } - gz_close(gunzip_pid); - fclose(uncompressed_stream); goto cleanup; } else if (strncmp(ar_magic, "\037\213", 2) == 0) { /* it's a gz file, let's assume it's an opkg */ @@ -687,6 +688,8 @@ char *deb_extract(const char *package_filename, FILE *out_stream, if (strncmp(tar_header->name, "./", 2) == 0) name_offset = 2; if (strcmp(ared_file, tar_header->name+name_offset) == 0) { + int gunzip_pid = 0; + FILE *uncompressed_stream; /* open a stream of decompressed data */ uncompressed_stream = gz_open(unzipped_opkg_stream, &gunzip_pid); if (uncompressed_stream == NULL) { @@ -703,15 +706,15 @@ char *deb_extract(const char *package_filename, FILE *out_stream, file_list); /*fprintf(stderr, __FUNCTION__ ":%d: unarchive complete\n", __LINE__);*/ free_header_tar(tar_header); - gz_close(gunzip_pid); fclose(uncompressed_stream); + gz_close(gunzip_pid); break; } seek_sub_file(unzipped_opkg_stream, tar_header->size); free_header_tar(tar_header); } - gz_close(unzipped_opkg_pid); fclose(unzipped_opkg_stream); + gz_close(unzipped_opkg_pid); /*fprintf(stderr, __FUNCTION__ ":%d: done\n", __LINE__);*/ goto cleanup; } else {