Skip to content

Commit

Permalink
Use pipe2 and O_CLOEXEC to fix multi-threading issues
Browse files Browse the repository at this point in the history
  • Loading branch information
kibook committed Dec 23, 2024
1 parent c0cad85 commit 298e74c
Showing 1 changed file with 81 additions and 56 deletions.
137 changes: 81 additions & 56 deletions tools/s1kd-appcheck/s1kd-appcheck.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#define _GNU_SOURCE

#include <stdio.h>
#include <string.h>
#include <unistd.h>
Expand All @@ -6,6 +8,7 @@
#include <errno.h>
#include <pthread.h>
#include <paths.h>
#include <fcntl.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
Expand Down Expand Up @@ -1026,8 +1029,20 @@ static void extract_assigns(xmlNodePtr asserts, xmlNodePtr product)
xmlXPathFreeContext(ctx);
}

/* Send an XML document to an external command's stdin, and (optionally) read an XML document from the command's stdout. */
static int exec_doc(xmlDocPtr doc, const char *cmd, xmlDocPtr *out)
/* Send an XML document to an external command's stdin, and return the exit status code. */
static int execute_command_on_doc(xmlDocPtr doc, const char *cmd)
{
FILE *p;
p = popen(cmd, "w");
if (p == NULL) {
fprintf(stderr, E_PIPE, cmd, strerror(errno));
}
xmlDocDump(p, doc);
return pclose(p);
}

/* Send an XML document to an external command's stdin, and read an XML document from the command's stdout. */
static int execute_command_on_doc_and_parse_output(xmlDocPtr doc, const char *cmd, xmlDocPtr *out)
{
/* Pipe to write to the stdin of the command. */
int stdin_pipe[2];
Expand All @@ -1039,44 +1054,41 @@ static int exec_doc(xmlDocPtr doc, const char *cmd, xmlDocPtr *out)
int pid;

/* Create a pipe for stdin. Abort on error. */
if (pipe(stdin_pipe) == -1) {
fprintf(stderr, E_PIPE, cmd, strerror(errno));
exit(EXIT_PIPE);
if (pipe2(stdin_pipe, O_CLOEXEC) < 0) {
goto pipe_error;
}

/* Create a pipe for stdout. Abort on error. */
if (pipe(stdout_pipe) == -1) {
close(stdin_pipe[0]);
close(stdin_pipe[1]);
fprintf(stderr, E_PIPE, cmd, strerror(errno));
exit(EXIT_PIPE);
if (pipe2(stdout_pipe, O_CLOEXEC) < 0) {
goto pipe_error;
}

/* Create a child process to execute the command. */
pid = fork();

/* If there is an error forking, abort. */
if (pid == -1) {
close(stdin_pipe[0]);
close(stdin_pipe[1]);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
fprintf(stderr, E_PIPE, cmd, strerror(errno));
exit(EXIT_PIPE);
goto pipe_error;
}

/* The child process executes the given command with stdin and stdout directed to the created pipes. */
if (pid == 0) {
/* Direct stdin and stdout to the created pipes. */
if (dup2(stdin_pipe[0], STDIN_FILENO) == -1 || dup2(stdout_pipe[1], STDOUT_FILENO) == -1) {
fprintf(stderr, E_PIPE, cmd, strerror(errno));
/* Close the ends of the pipes we don't use in the child process. */
if (close(stdin_pipe[1]) == -1 || close(stdout_pipe[0]) == -1) {
goto child_pipe_error;
}

/* Close the ends of the pipes now that they have been dup'd. */
close(stdin_pipe[0]);
close(stdin_pipe[1]);
close(stdout_pipe[0]);
close(stdout_pipe[1]);
/* Direct stdin and stdout to the created pipes, and close the dup'd pipe ends. */
if (stdin_pipe[0] != STDIN_FILENO) {
if (dup2(stdin_pipe[0], STDIN_FILENO) == -1 || close(stdin_pipe[0]) == -1) {
goto child_pipe_error;
}
}
if (stdout_pipe[1] != STDOUT_FILENO) {
if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1 || close(stdout_pipe[1]) == -1) {
goto child_pipe_error;
}
}

/* Print an info message with the command in DEBUG verbosity. */
if (verbosity >= DEBUG) {
Expand All @@ -1086,32 +1098,36 @@ static int exec_doc(xmlDocPtr doc, const char *cmd, xmlDocPtr *out)
/* Replace the child process with the execution of the command. */
execl(_PATH_BSHELL, "sh", "-c", cmd, (char *) NULL);

/* If the exec fails, abort. */
/* If for some reason exec fails, abort. */
child_pipe_error:
fprintf(stderr, E_PIPE, cmd, strerror(errno));
exit(EXIT_PIPE);
_exit(EXIT_FAILURE);
}

/* Parent process continues here. */

/* Close the ends of the pipes we don't use in the parent process. */
close(stdin_pipe[0]);
close(stdout_pipe[1]);
if (close(stdin_pipe[0]) == -1 || close(stdout_pipe[1]) == -1) {
goto pipe_error;
}

/* Write the XML document to the stdin of the child process. */
xmlSaveCtxtPtr ctxt = xmlSaveToFd(stdin_pipe[1], NULL, 0);
xmlSaveDoc(ctxt, doc);
xmlSaveClose(ctxt);

/* Close stdin of the child process. */
close(stdin_pipe[1]);

/* If out is not NULL, read the stdout of the child process into a new XML document. */
if (out) {
*out = xmlReadFd(stdout_pipe[0], NULL, NULL, 0);
if (close(stdin_pipe[1]) == -1) {
goto pipe_error;
}

/* Read the stdout of the child process into a new XML document. */
*out = xmlReadFd(stdout_pipe[0], NULL, NULL, XML_PARSE_NOERROR);

/* Close the stdout of the child process. */
close(stdout_pipe[0]);
if (close(stdout_pipe[0]) == -1) {
goto pipe_error;
}

/* Wait for the child process to finish to get its exit status. */
int status;
Expand All @@ -1123,28 +1139,32 @@ static int exec_doc(xmlDocPtr doc, const char *cmd, xmlDocPtr *out)
/* If the child process returned normally, return the exit status. Otherwise, abort. */
if (WIFEXITED(status)) {
return WEXITSTATUS(status);
} else {
fprintf(stderr, E_PIPE, cmd, "Child process exited abnormally");
exit(EXIT_PIPE);
}

/* Abort if there are any errors creating/executing the child process. */
pipe_error:
fprintf(stderr, E_PIPE, cmd, strerror(errno));
exit(EXIT_PIPE);
}

/* Add error elements from reports to the collection of errors. */
static void add_errors_from_report(xmlNodePtr errors, xmlDocPtr report, const char *cmd)
static void add_errors_from_report(const xmlNodePtr errors, const xmlDocPtr report, const xmlChar *cmd)
{
xmlNodePtr cmd_node;
xmlXPathContextPtr ctx;
xmlXPathObjectPtr obj;

cmd_node = xmlNewChild(errors, NULL, BAD_CAST "validator", NULL);
xmlSetProp(cmd_node, BAD_CAST "command", cmd);

ctx = xmlXPathNewContext(report);
obj = xmlXPathEvalExpression(BAD_CAST "//error", ctx);

if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;

for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
xmlNodePtr err;
err = xmlAddChild(errors, xmlCopyNode(obj->nodesetval->nodeTab[i], 1));
xmlSetProp(err, BAD_CAST "command", BAD_CAST cmd);
xmlAddChild(cmd_node, xmlCopyNode(obj->nodesetval->nodeTab[i], 1));
}
}

Expand Down Expand Up @@ -1206,7 +1226,12 @@ static int check_assigns(xmlDocPtr doc, const char *path, xmlNodePtr asserts, xm
xmlFree(v);
}

exec_doc(doc, filter_cmd, &filtered_doc);
execute_command_on_doc_and_parse_output(doc, filter_cmd, &filtered_doc);

/* If the filtered doc is empty, the doc was not applicable to the given assigns. */
if (filtered_doc == NULL) {
return 0;
}

if (opts->include_errors) {
errors = xmlNewNode(NULL, BAD_CAST "errors");
Expand All @@ -1222,15 +1247,15 @@ static int check_assigns(xmlDocPtr doc, const char *path, xmlNodePtr asserts, xm
if (opts->include_errors) {
xmlDocPtr report = NULL;

e += exec_doc(filtered_doc, (char *) cmd, &report);
e += execute_command_on_doc_and_parse_output(filtered_doc, (char *) cmd, &report);

if (report) {
add_errors_from_report(errors, report, (char *) cmd);
add_errors_from_report(errors, report, cmd);
}

xmlFreeDoc(report);
} else {
e += exec_doc(filtered_doc, (char *) cmd, NULL);
e += execute_command_on_doc(filtered_doc, (char *) cmd);
}

xmlFree(cmd);
Expand All @@ -1241,9 +1266,9 @@ static int check_assigns(xmlDocPtr doc, const char *path, xmlNodePtr asserts, xm

/* Schema validation */
if (opts->include_errors) {
strcpy(cmd, DEFAULT_VALIDATE " -ex");
strcpy(cmd, DEFAULT_VALIDATE " -x");
} else {
strcpy(cmd, DEFAULT_VALIDATE " -e");
strcpy(cmd, DEFAULT_VALIDATE);
}

switch (verbosity) {
Expand All @@ -1261,23 +1286,23 @@ static int check_assigns(xmlDocPtr doc, const char *path, xmlNodePtr asserts, xm
if (opts->include_errors) {
xmlDocPtr validate_report = NULL;

e += exec_doc(filtered_doc, cmd, &validate_report);
e += execute_command_on_doc_and_parse_output(filtered_doc, cmd, &validate_report);

if (validate_report) {
add_errors_from_report(errors, validate_report, cmd);
add_errors_from_report(errors, validate_report, BAD_CAST cmd);
}

xmlFreeDoc(validate_report);
} else {
e += exec_doc(filtered_doc, cmd, NULL);
e += execute_command_on_doc(filtered_doc, cmd);
}

/* BREX validation */
if (opts->brexcheck) {
if (opts->include_errors) {
strcpy(cmd, DEFAULT_BREXCHECK " -celx");
strcpy(cmd, DEFAULT_BREXCHECK " -clx");
} else {
strcpy(cmd, DEFAULT_BREXCHECK " -cel");
strcpy(cmd, DEFAULT_BREXCHECK " -cl");
}

strcat(cmd, " -d '");
Expand All @@ -1303,15 +1328,15 @@ static int check_assigns(xmlDocPtr doc, const char *path, xmlNodePtr asserts, xm
if (opts->include_errors) {
xmlDocPtr brexcheck_report = NULL;

e += exec_doc(filtered_doc, cmd, &brexcheck_report);
e += execute_command_on_doc_and_parse_output(filtered_doc, cmd, &brexcheck_report);

if (brexcheck_report) {
add_errors_from_report(errors, brexcheck_report, cmd);
add_errors_from_report(errors, brexcheck_report, BAD_CAST cmd);
}

xmlFreeDoc(brexcheck_report);
} else {
e += exec_doc(filtered_doc, cmd, NULL);
e += execute_command_on_doc(filtered_doc, cmd);
}
}
}
Expand Down Expand Up @@ -2431,7 +2456,7 @@ int main(int argc, char **argv)
/* check_duplicate */ false,
/* rem_delete */ false,
/* mode */ STANDALONE,
/* xml */ false
/* include_errors */ false
};

int err = 0;
Expand Down

0 comments on commit 298e74c

Please sign in to comment.