From 96abdb5f78052b77ec29b1c2042bdfcffd21b666 Mon Sep 17 00:00:00 2001 From: Jack Rann Date: Sat, 28 Sep 2024 16:59:06 -0700 Subject: [PATCH] Handle Python execution via execvp(). New test to validate single quote handling in argument. --- src/engines/python-engine.cc | 25 +++++++++++++++++-------- tests/python/echo.py | 10 ++++++++++ tests/tools/test_python.py | 11 +++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100755 tests/python/echo.py diff --git a/src/engines/python-engine.cc b/src/engines/python-engine.cc index 3aa5c3e4..1575c973 100644 --- a/src/engines/python-engine.cc +++ b/src/engines/python-engine.cc @@ -96,16 +96,25 @@ class PythonEngine : public ScriptEngineBase const char **argv = conf.getArgv(); unsigned int argc = conf.getArgc(); - std::string s = fmt("%s %s ", command.c_str(), kcov_python_path.c_str()); - for (unsigned int i = 0; i < argc; i++) - s += "'" + std::string(argv[i]) + "' "; - - int res; + // Make a copy of the vector, with python + helper script first + char **vec; + int argcStart = 2; + vec = (char **) xmalloc(sizeof(char *) * (argc + 3)); + vec[0] = xstrdup(command.c_str()); + vec[1] = xstrdup(kcov_python_path.c_str()); + + for (unsigned i = 0; i < argc; i++) + vec[argcStart + i] = xstrdup(argv[i]); + + /* Execute the script */ + if (execvp(vec[0], vec)) + { + perror("Failed to execute python helper"); - res = system(s.c_str()); - panic_if(res < 0, "Can't execute python helper"); + free(vec); - exit(WEXITSTATUS(res)); + return false; + } } else if (m_child < 0) { diff --git a/tests/python/echo.py b/tests/python/echo.py new file mode 100755 index 00000000..b702ac69 --- /dev/null +++ b/tests/python/echo.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +import sys + +def main(): + print('echo:', sys.argv) + + +if __name__ == '__main__': + main() diff --git a/tests/tools/test_python.py b/tests/tools/test_python.py index ca379f1c..de7b511b 100644 --- a/tests/tools/test_python.py +++ b/tests/tools/test_python.py @@ -190,3 +190,14 @@ def runTest(self): dom = cobertura.parseFile(self.outbase + "/kcov/main/cobertura.xml") assert cobertura.hitsPerLine(dom, "second.py", 57) == 1 assert cobertura.hitsPerLine(dom, "second.py", 61) == 1 + + +class python_single_quote_arg(libkcov.TestCase): + def runTest(self): + rv, o = self.do( + self.kcov + " " + self.outbase + "/kcov " + self.sources + "/tests/python/echo.py 1 two a'b" + ) + + assert b"1" in o + assert b"two" in o + assert b"a'b" in o