From e0faaa4c4b61130bd7af5b887e9fc90d4e3dbf77 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Wed, 20 Feb 2019 01:17:06 -0800 Subject: [PATCH] parser: Fix parser failing to handle errors when setting up work The parser is not correctly handling some error conditions when dealing with work units. Failure to spawn work, access files, etc should be returned where appropriate, and be able to abort processing if abort_on_error is set. In addition some errors are leading to a direct exit without checking for abort_on_error. BugLink: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=921866 BugLink: http://bugs.launchpad.net/bugs/1815294 Signed-off-by: John Johansen Acked-by: Eric Chiang (cherry picked from 2.12 commit 7cd903dd20758ae63f0426c7d9135c42f809b3b2) --- parser/parser.h | 14 +++++++++-- parser/parser_main.c | 57 ++++++++++++++++++++++++++++++++------------ 2 files changed, 54 insertions(+), 17 deletions(-) diff --git a/parser/parser.h b/parser/parser.h index e7acda609..5643f5518 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -171,13 +171,23 @@ extern int preprocess_only; #ifdef DEBUG -#define PDEBUG(fmt, args...) fprintf(stderr, "parser: " fmt, ## args) +#define PDEBUG(fmt, args...) \ +do { \ + int pdebug_error = errno; \ + fprintf(stderr, "parser: " fmt, ## args); \ + errno = pdebug_error; \ +} while (0) #else #define PDEBUG(fmt, args...) /* Do nothing */ #endif #define NPDEBUG(fmt, args...) /* Do nothing */ -#define PERROR(fmt, args...) fprintf(stderr, fmt, ## args) +#define PERROR(fmt, args...) \ +do { \ + int perror_error = errno; \ + fprintf(stderr, fmt, ## args); \ + errno = perror_error; \ +} while (0) #ifndef TRUE #define TRUE (1) diff --git a/parser/parser_main.c b/parser/parser_main.c index 95235901f..7b796f423 100644 --- a/parser/parser_main.c +++ b/parser/parser_main.c @@ -899,8 +899,11 @@ do { \ work_sync_one(RESULT); \ } while (0) +/* returns -1 if work_spawn fails, not a return value of any unit of work */ #define work_spawn(WORK, RESULT) \ -do { \ +({ \ + int localrc = 0; \ + do { \ /* what to do to avoid fork() overhead when single threaded \ if (jobs == 1) { \ // no parallel work so avoid fork() overhead \ @@ -937,11 +940,17 @@ do { \ fprintf(stderr, " JOBS SPAWN: created %ld ...\n", njobs); \ } else { \ /* error */ \ - if (debug_jobs) \ - fprintf(stderr, " JOBS SPAWN: failed error: %d) ...\n", errno); \ + if (debug_jobs) { \ + int error = errno; \ + fprintf(stderr, " JOBS SPAWN: failed error: %d) ...\n", errno); \ + errno = error; \ + } \ RESULT(errno); \ + localrc = -1; \ } \ -} while (0) + } while (0); \ + localrc; \ +}) /* sadly C forces us to do this with exit, long_jump or returning error @@ -1018,11 +1027,15 @@ static int profile_dir_cb(int dirfd unused, const char *name, struct stat *st, if (!S_ISDIR(st->st_mode) && !is_blacklisted(name, NULL)) { struct dir_cb_data *cb_data = (struct dir_cb_data *)data; autofree char *path = NULL; - if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0) + if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0) { PERROR(_("Out of memory")); - work_spawn(process_profile(option, cb_data->kernel_interface, - path, cb_data->cachedir), - handle_work_result); + handle_work_result(errno); + return -1; + } + rc = work_spawn(process_profile(option, + cb_data->kernel_interface, + path, cb_data->cachedir), + handle_work_result); } return rc; } @@ -1036,11 +1049,15 @@ static int binary_dir_cb(int dirfd unused, const char *name, struct stat *st, if (!S_ISDIR(st->st_mode) && !is_blacklisted(name, NULL)) { struct dir_cb_data *cb_data = (struct dir_cb_data *)data; autofree char *path = NULL; - if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0) + if (asprintf(&path, "%s/%s", cb_data->dirname, name) < 0) { PERROR(_("Out of memory")); - work_spawn(process_binary(option, cb_data->kernel_interface, - path), - handle_work_result); + handle_work_result(errno); + return -1; + } + rc = work_spawn(process_binary(option, + cb_data->kernel_interface, + path), + handle_work_result); } return rc; } @@ -1157,11 +1174,14 @@ int main(int argc, char *argv[]) } /* skip stdin if we've seen other command line arguments */ if (i == argc && optind != argc) - continue; + goto cleanup; if (profilename && stat(profilename, &stat_file) == -1) { + last_error = errno; PERROR("File %s not found, skipping...\n", profilename); - continue; + if (abort_on_error) + break; + goto cleanup; } if (profilename && S_ISDIR(stat_file.st_mode)) { @@ -1176,20 +1196,27 @@ int main(int argc, char *argv[]) cb = binary_input ? binary_dir_cb : profile_dir_cb; if ((retval = dirat_for_each(AT_FDCWD, profilename, &cb_data, cb))) { + last_error = errno; PDEBUG("Failed loading profiles from %s\n", profilename); + if (abort_on_error) + break; } } else if (binary_input) { + /* ignore return as error is handled in work_spawn */ work_spawn(process_binary(option, kernel_interface, profilename), handle_work_result); } else { + /* ignore return as error is handled in work_spawn */ work_spawn(process_profile(option, kernel_interface, profilename, cacheloc), handle_work_result); } - if (profilename) free(profilename); + cleanup: + if (profilename) + free(profilename); profilename = NULL; } work_sync(handle_work_result);