6

Let's say, I have two programs - input.c & output.c All I want to do is send some payload/characters in "half pyramid" format into another one using execl() function.

input.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

#define SIZE 1024

int length;

int main(int argc, char *argv[])
{
    pid_t pid;
    char *target;
    //char payload[length+1];
    char payload[SIZE + 1];
    int status;
    int i = 0;

    if(argc < 2)
    {
        printf("Usage %s <length> <target>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    length = atoi(argv[1]);
    target = argv[2];

    while(i < length)
    {
        pid = fork();

        if(pid != 0)
        {
            waitpid(-1, &status, 0);
            //exit(0);
        }

        if(pid == 0)
        {
            payload[i] = 'A';
            payload[i + 1] = '\0';
            execl(target, target, payload, NULL);
            //printf("%s\n", payload);
        }
        ++i;
    }
    return 0;
}

Commented passages are just for debugging purposes. Because As you can see (while trying), when you want just print it, everything works properly.

output.c (or if you want 'target.c')

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char buffer[30];
    strncpy(buffer, argv[1], sizeof(buffer));
    printf("Output: %s\n", buffer);

    return 0;
}

When I compile input.c like:

gcc input.c -o input

& output.c:

gcc output.c -o output

Ok. Now, everything is prepared. Let's say, I'd like to send a payload - length 6

./input 6 ./output

but all I get on output is just this (or simply with another junks characters):

Output: A
Output: 0A
Output: 0,A
Output: 0,�A
Output: 0,�8A
Output: 0,�8�A

I tried so many things, but all of them failed and output was still the same, as you can see above.

I'd be very grateful if anyone can help me and possibly show me where is problem. Can be problem in using fork() and execl() functions together?

Yeez
  • 282
  • 1
  • 3
  • 9

2 Answers2

5

Got it, you should not update payload in the child block code...

Here's a fix (cannot test it now) :

 while(i < length)
    {
        pid = fork();
        payload[i] = 'A';
        payload[i + 1] = '\0';

        if(pid != 0)
        {
            waitpid(-1, &status, 0);
            //exit(0);
        }

        if(pid == 0)
        {
            execl(target, target, payload, NULL);
            //printf("%s\n", payload);
        }
        ++i;
    }

[removed unrelated sentence]

EDIT (additional explanations) : payload updating must be in both parent and child code. If you don't understand why, I can add more explanation.

EDIT2 (as requested). You want update payload for the next forked child process. In your code, all child code is replaced by execl() into target code. So the fork() is executed only by the first parent process (the root one).

You have to update payload by the first parent and make it accessible too all the children. Putting it into this block won't work either :

// block only executed by the first parent.
if(pid != 0)
{
    waitpid(-1, &status, 0);
}

Therefore, You must update it in a place both accessible by the parent and the child : after the fork(), before if(pid == 0) block.

In your code, you increment i in the common block, but the parent's payload is never updated. So in the child block, just before the execl(), your adding 'A\0' at the end of an uninitialized C string.

Amessihel
  • 5,891
  • 3
  • 16
  • 40
  • You are a man! Yes, it works like a charm. Thanks a lot for your help. And yes, if it's not problem for you, I'd be grateful for some more detailed explanation, because I don't think I understand it on 100% and I don't want to do some similar error in future. – Yeez Apr 16 '15 at 16:38
  • Ok, I add some explanations. It was a good "refresh" workout for me. – Amessihel Apr 16 '15 at 16:39
  • Hehe, I'm glad it was so. Anyway, now I'm checking it one more time and I have one additional question - if you can answer. In case that I replace strncpy with strcpy and do "./input 80 ./output", why I cant see somewhere on output "Segmentation fault"? I'm just curious. – Yeez Apr 16 '15 at 16:57
  • @Yeez Is it more comprensible ? Because argv[1] is a well-terminated string and `./output` size is less than 30. – Amessihel Apr 16 '15 at 17:01
  • I understand it. But I'm thinking about when I will send there length > 30 and I'd like to see, when program fails. In this case are just executed all iterations and doesn't matter if it's 40 or 80 - because I can't see any error. Do you understand what I want to say? – Yeez Apr 16 '15 at 17:18
  • @Yeez You'll get no errors as long as you write on memory allocated to your process... That's all I can say, The behaviour is not predictible. You can also have a peek on this [SO topic](http://stackoverflow.com/questions/28352371/why-no-segmentation-fault-when-strcpy-causes-an-buffer-overflow). – Amessihel Apr 16 '15 at 17:34
  • Okay, I'll check it. That's all from my part. Thank you again for great help. – Yeez Apr 16 '15 at 17:44
2

When your program forks, it creates a new process. This new process, after if(pid == 0), alters the payload, and runs exec (ie executes output, which then dies. I.e, your change of payload stays in the child, and does not affect the parent process. Only ++i does, which is why you're seeing unitialized data.

Move payload change before fork (or at least out of child-only block), so it's in the parent as well.

che
  • 12,097
  • 7
  • 42
  • 71