0

I am trying to make an online judge for c programming. When user enters the c code and submits it, my form redirects to judge.php which is the action file for the form. Here is what I have written in judge.php

<?php 
$text=$_POST['code'];
//echo $text;

$var_str = var_export($text, true);

file_put_contents('code.c', $text);

$ans=exec('pwd');

$ans= exec('gcc code.c');

echo $ans;

?>

I have captured user input in $text and wrote it to a c file(code.c). Till now, it is fine. But exec(gcc code.c) is not working and not giving any output. I tried other linux commnads like pwd, date, etc. They are working fine. What may be the reason for this and how to fix it? It is not a directory issue i tried exec(pwd) and it gave the output as the same directory in which code is present. I tried to run same code.c file from terminal and it is running fine. So, it is also not a 'permission' problem.

One more thing, how to echo the error message generated if any exec() command is not working properly?

After getting suggestion from the answer below, i tried $cmd="gcc -std=c99 code.c -g -Wall mysql_config --libs --cflags -o db_obj.o --pedantic";

exec($cmd,$out,$status);

But it is also not working. The status returned is 1

Most probably it is permission issue. "whoami" says nobody. Please tell how to change the owner from nobody to root or how to assign the permission to execute gcc from nobody

1
  • Personally I'd never let anyone compile directly from the web especially not using the exec() function so so so dangerous. etc. I'd batch/queue the code and have a cron job compile and then email them a download link for the compiled application after its completed. A better way to do this would be to use a proper build environment based around git/svn or something similar users put their code into the repos and it builds it and returns it to their repos in a build folder. Commented Jan 15, 2014 at 11:53

2 Answers 2

3

Three main aspects to my answer

improper use of the exec function.

Look at the man pages. First, the exec function's signature is:

string exec ( string $command [, array &$output [, int &$return_var ]] )

So exec can take up to 3 arguments. It returns the last line of the command's output, like the docs state quite clearly:

The last line from the result of the command. If you need to execute a command and have all the data from the command passed directly back without any interference, use the passthru() function.

To get the output of the executed command, be sure to set and use the output parameter.

So in your case:

$lastLine = exec($command, $fullOutput, $status);

Is what you're looking for. If $status is anything else than 0, your command was unsuccessful. That's what you should check to react accordingly.
The full output of any command can be found in $fullOutput as a line-per-line array.
Output like:

all went well
except for this

Will look like this in the $fullOutput array:

array('all went well', 'except for this');

permissions can be an issue, still.

You say permissions aren't likely to be the cause of the problem, because you can run gcc from the command-line. All fine and dandy, but what user is running the PHP script on the server?
In the case of web-servers, that user is often called nobody, apache or something, and that user is very likely not permitted to run gcc. It's PHP that runs a new instance of whatever default shell it has set up (bash, probably), and it's PHP's user that logs in to that shell, and it's that user that is calling gcc...

Know who you are, and what groups you belong to. Try adding this to your script:

echo 'Script is running under user: ', exec('whoami'), '<br>', PHP_EOL;
echo 'member of the following groups: ', exec('groups'), '<br>', PHP_EOL;

And before you ask: yes, those are comma's... no need to concatenate, you can pass multiple variables/values to echo, separated by a comma. It's actually faster (think of it as C++'s std::cout << some_var << another_var;)

general issues + security

This all said and done: compiling C code from a php script isn't as simple as you seem to think it is. Suppose I were to write this:

#include <stdio.h>
#include <time.h>
int main ( void )
{
    time_t t = time(NULL);
    if (t%2)
    {
        float val = (float) t/2.0;
        //do stuff with float...
    }
    else
    {
        unsigned long long val = t/2;
        //do stuff with unsigned long long...
    }
}

Your gcc test.c command would fail, because you failed to pass the argument -std=c99, for example.
If I wanted a script to compile a given file, I'd also expect that script to allow me to choose which arguments I compiled my code with, too -g, -Wall and, not to mention: cflags and libs (the output of pkg-config or mysql_config --cflags --libs, to name a specific example I recently used).
Basically, your script simply cannot deal with my wanting to compile something with a commind like

gcc -std=c99 code.c -g -Wall `mysql_config --libs --cflags` -o db_obj.o --pedantic

Which still is a simplified version of what many compilation commands look like, especially when debugging code under development. For stable releases, you'd probably drop -g and --pedantic, but you get my point...
Just think of what it means, allowing the user to pass a set of cli arguments, along with the code. They might pass an argument like -DSOME_MACRO or -O0, which means they might also pass -O0 && rm -Rf *. That means you'll have to call escapeshellcmd or escapeshellarg. Both of which will prohibit me from passing a valid argument, being:

`mysql_config --libs --cflags`

Which contains back-ticks, and thus will be escaped.

To be frank, I struggle to see the point of this exercise... and I'm leaving a lot out, still: the dangers of compiling (let alone running) user-provided code on your machine, for example, are not to be overlooked. You can't just compile code, and run it on your server: memory leaks, segfaults... heck, pure evil code is all getting compiled on your server unchecked if this is the code you have. Really, save yourself a lot of tears, and include an iframe that loads codepad or some similar service...


Recap:

  • always check the man for a function, see if you're getting all information it returns
  • check the permissions and runtime for the user that is actually executing the commands
  • Never trust the network, don't blindly assume people will submit valid, harmless code for you to compile.
  • Don't reinvent the wheel: compilation services exist, just forward those (but ask for permission first)
Sign up to request clarification or add additional context in comments.

8 Comments

its fine. I understand the risks. But Please elaborate on the argument that i missed. To be specific, what change will make my program compile?
@SuperCoder: In the case of the C snippet I put up, the command to compile it should look like this gcc code.c -std=c99, but I'd prefer to compile it using gcc code.c -std=c99 -g -Wall for debugging, and without the -g flag once I'm done debugging. If I'm using #include <mysql/mysq.h>, my command would probably look more like gcc code.c -std=c99 mysql_config --cflags --libs -g -Wall -o db_obj.o where mysql_config --cflags --libs should be surrounded by back-ticks, too
I've added an example of a compile command I might actually want to use. Note that it's not even insanely complex: I'm just compiling a single source file, that includes the mysql C api, and I'm compiling it as an object that can be linked to other files
I tried to compile your c code as well as the compile code you suggested but its not working still. the status returned is 1.
@SuperCoder: I didn't test the code, and it was missing a return 0 statement... But before trying to exec gcc, find out what user is running PHP, and what groups he belongs to (commands for that are whoami and groups respectively), then check if the user is allowed to run gcc. Once you've done that, and set the permissions accordingly, try again
|
0

Try this code to execute c program from PHP file

<?php 
    // used to compile the c file using exec() in php
    exec('gcc helloworld.c -o helloworld', $out, $status); 
    if (0 === $status) {
        var_dump($out);
        // used to execute the c file using exec() in php
        exec('./helloworld', $out, $status);
        if (0 === $status) {
            var_dump($out);
        } else {
            echo "Command failed with status: $status";
        }
    } else {
        echo "Command failed with status: $status";
    }
?>

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.