1

I have read this: Stop shell wildcard character expansion? and similar - this is not a duplicate question.

I am dealing with a set of legacy c++ code, from which a number of binaries are built. This code uses system function to execute a shell script passing it some arguments. Unfortunately, as it turned out, some arguments may include * character, yet arguments are not properly escaped. As a result, when the shell script is execute, the * gets expanded, which is not what I want. Here's a simple code to replicate the issue:

script.sh

#!/bin/bash

i=1
for var in "$@"
do
    echo "$i => $var"
    ((i++))
done

program.cpp

#include <stdlib.h>
#include <string>

int main(int argc, char* argv[])
{
   std::string command = "/tmp/sh/script.sh *";
   return system(command.c_str());
}

When the compiled binary executes, I get the list of files as parameters to the shell, instead of the asterisk:

$ ./program
1 => program
2 => program.cpp
3 => script.sh

Now, if I turn off glob expansion in shell using set -f or set -o noglob, then calling the script directly works as expected:

$ set -f
$ ./script.sh *
1 => *

However when the script is executed with system, this doesn't work:

$ set -f
$ ./program
1 => program
2 => program.cpp
3 => script.sh

Is there any way to prevent the expansion when command is executed via system?

I really want to avoid having to rebuild the binaries, as operationally and managerially it would be an enormous amount of work.

UPDATE: As I mentioned, I want to avoid any changes to the cpp source; I want to simply disable glob expansion on the OS level (via any flags, changed files, etc. - I'm happy to modify any files on the box if needed).

Aleks G
  • 56,435
  • 29
  • 168
  • 265
  • 1
    You can shadow library `system()` function with your own. – Slava Jun 13 '17 at 14:41
  • Doesn't the shell let you execute two commands on the same line? – Captain Obvlious Jun 13 '17 at 14:41
  • use `"set -f; script.sh *"` or call `set -f` inside the script? – Jean-Baptiste Yunès Jun 13 '17 at 14:42
  • @Jean-BaptisteYunès The first would require changing cpp source and rebuilding; the second isn't going to work, because expansion happens before the script executes. – Aleks G Jun 13 '17 at 14:42
  • @CaptainObvlious I'm not sure what you mean. Yes, it does, but which two commands and where? – Aleks G Jun 13 '17 at 14:43
  • @Slava That would require changing the source and rebuilding the executables – Aleks G Jun 13 '17 at 14:44
  • 1
    @AleksG no that does not require changing source, only relinking. You can avoid relinking as well if you can inject shared lib. – Slava Jun 13 '17 at 14:47
  • Then search if shell launched from `system()` uses any `.profile` startup file. – Jean-Baptiste Yunès Jun 13 '17 at 14:47
  • @Slava Ok, fair point, only relinking - but I would have to go through a release process of the new binaries, which is an enormous amount of work. – Aleks G Jun 13 '17 at 14:50
  • 1
    You can create a shared lib with your own `system()` function and add it by LD_PRELOAD var – Slava Jun 13 '17 at 14:50
  • Consider *not* using `system()` at all. You don't know what shell or environment you'll get and it's quite prone to security issues (for example; it inherits your processes permissions). A solution with `fork()` + one of the calls from the `exec*()` family​ offers far more control and safety. – Jesper Juhl Jun 13 '17 at 14:56
  • @JesperJuhl You seem to miss the main question: how to do it **without** changes to the application. The source code is several megabytes (millions of lines); there are multiple branches for different versions; `system` is used a hundred of times in the code. – Aleks G Jun 13 '17 at 14:59
  • 2
    @Aleks G. I did see that. But sometimes it's worth it to refactor a bunch of code to arrive at a better solution going forward. :-) – Jesper Juhl Jun 13 '17 at 15:01
  • LD_PRELOAD definitely seems like the way to go. You might also be able to extract the original command from `/proc//cmdline`, and since you're willing to modify system files, you can replace `/bin/sh` with a wrapper – that other guy Jun 13 '17 at 18:43
  • @Slava After some investigation, I got LD_PRELOAD trick to work. Post that as an answer and I will accept. – Aleks G Jun 15 '17 at 07:41
  • Just post whatever you did with enough code to reproduce it, that'll be more helpful to future readers than merely our suggested directions ^^ – that other guy Jun 15 '17 at 18:22
  • @Slava I ended up using LD_PRELOAD - and it worked quite well. If you post that as an answer, I'll accept. – Aleks G Aug 30 '17 at 15:25

1 Answers1

0

In the interest of closing the loop, as Slava didn't post the answer... Copied from his comment:

You can create a shared lib with your own system() function and add it by LD_PRELOAD var

I ended up using this method - and it worked perfectly fine.

Aleks G
  • 56,435
  • 29
  • 168
  • 265