Subprocess execution with protection against parameter modification

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|

Subprocess execution with protection against parameter modification

sur-behoffski
G'day,

[This message should be threaded, but I read the list in
Digest mode, so apologies for any thread breakage.]

A query came up in the last couple of days regarding
executing commands but protecting parameters against
unwanted modification by special character substitution
by the shell.

I'm on GNU/Linux; not sure if the following suggestion
works on Windows(R).

I use the Lua Rock "luaposix", which contains a slew of
functions to work with the Posix interface of an OS.
Subprocesses can be set up by:

     Facilitating stdin/stdout/stderr connections:
         posix.pipe()
         posix.dup2()
         posix.fileno()

     Having a single-word command (Cmd), plus all
     arguments in a single table (CmdlineArgs); each of
     these are interpreted as ASCII/UTF-8 C strings -- the
     first NUL ends the string.

     Using execp() in the child process to search the
     environment path for the command (e.g. "cp"), but
     otherwise making no changes to CmdlineArgs; and

     Parent:  Close the child end of the interconnecting
     pipes, and return the child's process ID (pid), along
     with the parent-end stdin/stdout/stderr file
     descriptors; and

     Child: Close the parent end of the interconnecting
     pipes, then use posix.dup2 to redirect the child's
     stdin/stdout/stderr to each go to its respective
     parent pipe; and

     Child: Use posix.execp to execute the command,
     together with its command-line arguments.

The code for this is part of a module, and is reproduced
below:

> --- Obtain pipes for stdin/stdout/stderr, fork the child process from
> -- the parent, and:
> --
> -- (a) **Child**: Set up its end of the pipes, and execute the specified
> -- command with the given arguments; or
> --
> -- (b) **Parent**: Close down the child end of the pipes, and hand details
> -- of the child's operation, e.g. pid, fd0, fd1 and fd2, to the caller.
> function M.raw_exec(Command, CmdlineArgs)
>         assert(Command, "missing command name")
>         CmdlineArgs = CmdlineArgs or {}
>
>         local Pipes = GetNonstdPipes(3)
>         local pid = posix.fork()
>         if pid < 0 then
>                 return nil, "raw_exec: Unable to fork(): "
>                         .. posix.errno(pid)
>         end
>         if pid ~= 0 then
>                 -- Parent: Close child end of pipes
>                 assert(posix.close(Pipes[1].Rd))
>                 assert(posix.close(Pipes[2].Wr))
>                 assert(posix.close(Pipes[3].Wr))
>
>                 -- Report PID and piped FDs to caller
>                 return pid, Pipes[1].Wr, Pipes[2].Rd, Pipes[3].Rd
>         end
>
>         -- Child: Close parent end of pipes
>         assert(posix.close(Pipes[1].Wr))
>         assert(posix.close(Pipes[2].Rd))
>         assert(posix.close(Pipes[3].Rd))
>
>         -- Change child's stdout/stdin/stderr to use its end of the
>         -- pipes.
>         assert(posix.dup2(Pipes[1].Rd, posix.fileno(io.stdin)))
>         assert(posix.dup2(Pipes[2].Wr, posix.fileno(io.stdout)))
>         assert(posix.dup2(Pipes[3].Wr, posix.fileno(io.stderr)))
>
>         -- Finally, execute the requested command in the child
>         -- environment.
>         local _, Errstr
>         _, Errstr = posix.execp(Command, CmdlineArgs)
>
>         -- Execp should overwrite the Lua interpreter and replace it
>         -- with the command, so, if Lua is still functioning here,
>         -- something went terribly wrong.  Try to retrieve an error
>         -- string (presumably from the command shell), and write it to
>         -- the parent's stderr pipe.
>         PosixUnistd.write(Pipes[3].Wr, Errstr)
>         io.flush()
>         os.exit(1)
> end

After that, the parent uses posix.poll to handle events
from the child, including gathering output, writing input
and handling the child-process-exit (HUP signal) case.  This
is called with a timeout, so it does not consume large
portions of CPU time polling file descriptors.

At the end, the results, especially including any child
output text, stderr text, and the exit status of the
child's program, are reported to the caller.

------------------------------------

The above is lifted almost literally from a demonstration
program in the luaposix package.  I've gathered the bits
into a module I've called "PosixExec", which makes life
easier for the user:

      local posix = require("posix")  -- optional

      local PE = require("PosixExec"  -- PosixExec.lua a "pseudo-rock"

      local Result
      local CmdlineArgs
      -- Dest comes from elsewhere

      CmdlineArgs {"-p", "--", Source"}
      if Dest == "" then
           Dest = "unknown-destination"
      end
      CmdlineArgs[#CmdlineArgs + 1] = Dest

      Result = PE.exec("cp", CmdlineArgs)
      PE.ValidateRun(Result)

      print("Child stdout: ", Result.stdout)
      print("Child stderr: ", Result.stderr)

There is an ancient version of PosixExec.lua floating around
the Internet already; if this list is strongly interested,
I'll post the current (MIT Licensed) version to this list;
which 375 lines, 15077 bytes.

(The latest uses std.normalize and std.strict, to provide
some standardization in the face of different Lua versions.)

----

cheers,

sur-behoffski (Brenton Hoff)
programmer, rouse Software
v
Reply | Threaded
Open this post in threaded view
|

Re: Subprocess execution with protection against parameter modification

v
On Tue, 2021-02-09 at 10:36 +1030, sur-behoffski wrote:

> G'day,
>
> [This message should be threaded, but I read the list in
> Digest mode, so apologies for any thread breakage.]
>
> A query came up in the last couple of days regarding
> executing commands but protecting parameters against
> unwanted modification by special character substitution
> by the shell.
>
> I'm on GNU/Linux; not sure if the following suggestion
> works on Windows(R).
>
> I use the Lua Rock "luaposix", which contains a slew of
> functions to work with the Posix interface of an OS.

Luaposix is indeed a good option for working with subprocesses, but it
is not available on Window. For windows, "winapi" module exists, which
is essentially a windows counterpart of luaposix.

If you want to use library both on POSIX and Windows systems, I'd
recommend looking at lua-subprocess module. Alternatively, you can
always write your own wrapper around luaposix and winapi that
encapsulates those into platform-independent API.
--
v <[hidden email]>
Reply | Threaded
Open this post in threaded view
|

Lua analog of Python's __ main__

Mike Nelson
Is there an easy way for a Lua script to determine whetherĀ  it was
launched directly from the interpreter/host program or it was required
into a another script?

Reply | Threaded
Open this post in threaded view
|

Re: Lua analog of Python's __ main__

Paul K-2
> Is there an easy way for a Lua script to determine whether  it was
> launched directly from the interpreter/host program or it was required
> into a another script?

See this discussion for several options and their pros/cons
(http://lua.2524044.n2.nabble.com/Modules-with-standalone-main-program-td7681497.html)
and this SO answer
(https://stackoverflow.com/questions/49375638/how-to-determine-whether-my-code-is-running-in-a-lua-module).

Paul.

On Tue, Feb 9, 2021 at 10:20 AM Michael Nelson <[hidden email]> wrote:
>
> Is there an easy way for a Lua script to determine whether  it was
> launched directly from the interpreter/host program or it was required
> into a another script?
>