I am making an online judge in django frame work I am trying to run a c program inside a docker container through subprocess. This is the line where I am getting error
x=subprocess.run('docker exec oj-cpp sh -c \'echo "{}" | ./a.out \''.format(problem_testcase.input),capture_output=True,shell=True)
here oj-cpp is my docker container name and problem_testcase.input is input for the c file
C code:
#include <bits/stdc .h>
using namespace std;
int main()
{
int t;
while (t--)
{
int a, b;
cin >> a >> b;
cout << a b << endl;
}
return 0;
}
problem_testcase.input:
2
4
5
3
5
error:
CompletedProcess(args='docker exec oj-cpp sh -c \'echo "2\n4\n5\n3\n5" | ./output\'', returncode=2, stdout=b'', stderr=b'2: 1: Syntax error: Unterminated quoted string\n')
I am not getting what's wrong is in my code
CodePudding user response:
subprocess.call(shell=True)
has some significant security problems and I'd avoid it whenever possible. It opens your application to a shell injection attack. You're actually seeing this in your code: if the string in problem_testcase.input
contains any characters that are meaningful to a shell, the shell will interpret them, which can cause it to do unexpected things.
In your input, Python expands the \n
newlines and then hands them off to the host shell it launches. So the shell command that's being run is
docker exec oj-cpp sh -c 'echo "2
4
5
3
5" | ./a.out '
and the shell stops at the first newline and tries to run the command up to there, which produces your error. If the input string can be controlled, and you know docker
commands work, it's actually straightforward to use this to take over the whole host.
The important thing to know here is that docker exec
will pass its own stdin to the program it's running. So you don't need the echo input | program
syntax; since you don't need that, you don't need the sh -c
invocation; and since you're only running a fixed command with no substitutions, you don't need shell=True
.
x = subprocess.run(['docker', 'exec', 'oj-cpp', './a.out'],
input=problem_testcase.input,
capture_output=True)
I might make two other changes to this program.
The Docker SDK for Python provides a more direct access to Docker functionality, without using subprocess
. It doesn't require a docker
binary, though it still requires the Docker daemon to be running and for you to have permission to access it.
I'd also avoid scripting docker exec
. If you think of a container as a wrapper around a single process, docker exec
starts a second process within that process, which is a little odd. If you docker run
a new (temporary) container, it starts up in a known state, and there's reduced risk of different program invocations interfering with each other.
import docker
client = docker.from_env()
# Start the container (`docker run -d -i some_image`)
container = client.containers.run(some_image, detach=True, stdin_open=True)
# Send the input string into the container
socket = container.attach_socket()
socket.send(problem_testcase.input.encode('utf-8'))
socket.close()
# Let the program run, collect its output, clean up
container.wait()
output = container.logs()
container.remove()