Skip to content

strange argv handling / mangled argv ? #6

@es-fabricemarie

Description

@es-fabricemarie

I'm running Fedora 40 in a UTM VM on MacOS, on an M1 chip.

The relevant software versions I have are:

binfmt-dispatcher-0.1.2-1.fc40.aarch64
fex-emu-2412-3.fc40.aarch64

To test my setup, I made a small ls equivalent in standard C, and compiled it under Fedora 40 x86_64. I then copy it into my as /var/tmp/fexls. The source code for that test is attached as fexls.c. It expects a path argument on the command line and lists the files at that path like ls would. Again this is just to test my fex setup.

When I run /var/tmp/fexls /var/tmp from my shell, I get this:

[binfmt_dispatcher] Using FEX
argc = 4
argv[0] = '/var/tmp/fexls'
argv[1] = '/var/tmp/fexls'
argv[2] = '/var/tmp/fexls'
argv[3] = '/var/tmp/'
Usage: /var/tmp/fexls <target-directory>

Why is my argv mangled?

When I run directly (without binfmt-dispatcher) FEXInterpreter /var/tmp/fexls /var/tmp/ I get the correct output:

argc = 2
argv[0] = '/var/tmp/fexls'
argv[1] = '/var/tmp/'
drwxr-xr-x   1 abrt abrt        0 Apr 16 03:17 abrt
-rw-r--r--   1 root root     2298 Apr 16 03:19 fstab
-rw-r--r--   1 root root      280 Apr 22 20:14 .location
.... [more files] ...

My binfmt-dispatcher configuration file /etc/binfmt-dispatcher.toml is standard (I just disabled muvm only):

[defaults]
interpreter = "fex"
log_level = "info"

[muvm]
path = "/usr/bin/muvm"

[interpreters.box64]
path = "/usr/bin/box64"

[interpreters.fex]
name = "FEX"
path = "/usr/bin/FEXInterpreter"
required_paths = ["/usr/share/fex-emu/RootFS/default.erofs"]
use_muvm = false

[interpreters.qemu]
name = "QEMU User space emulator"
path = "/usr/bin/qemu-x86_64"

[interpreters.qemu-static]
name = "QEMU User space emulator (static)"
path = "/usr/bin/qemu-x86_64-static"

Am I doing something wrong? Or is there a bug here?

The C code for fexls.c:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
#include <unistd.h>

void print_file_info(const char *path, const char *filename) {
    char fullpath[4096];
    snprintf(fullpath, sizeof(fullpath), "%s/%s", path, filename);

    struct stat st;
    if (lstat(fullpath, &st) == -1) {
        perror("lstat");
        return;
    }

    // File type and permissions
    char perms[11] = "----------";
    if (S_ISDIR(st.st_mode)) perms[0] = 'd';
    else if (S_ISLNK(st.st_mode)) perms[0] = 'l';
    else if (S_ISCHR(st.st_mode)) perms[0] = 'c';
    else if (S_ISBLK(st.st_mode)) perms[0] = 'b';
    else if (S_ISFIFO(st.st_mode)) perms[0] = 'p';
    else if (S_ISSOCK(st.st_mode)) perms[0] = 's';

    if (st.st_mode & S_IRUSR) perms[1] = 'r';
    if (st.st_mode & S_IWUSR) perms[2] = 'w';
    if (st.st_mode & S_IXUSR) perms[3] = 'x';
    if (st.st_mode & S_IRGRP) perms[4] = 'r';
    if (st.st_mode & S_IWGRP) perms[5] = 'w';
    if (st.st_mode & S_IXGRP) perms[6] = 'x';
    if (st.st_mode & S_IROTH) perms[7] = 'r';
    if (st.st_mode & S_IWOTH) perms[8] = 'w';
    if (st.st_mode & S_IXOTH) perms[9] = 'x';

    // User and group names
    struct passwd *pw = getpwuid(st.st_uid);
    struct group *gr = getgrgid(st.st_gid);

    // Modification time
    char timebuf[64];
    struct tm *mtm = localtime(&st.st_mtime);
    strftime(timebuf, sizeof(timebuf), "%b %e %H:%M", mtm);

    // Output format similar to ls -l
    printf("%s %3lu %s %s %8ld %s %s\n",
           perms,
           st.st_nlink,
           pw ? pw->pw_name : "???",
           gr ? gr->gr_name : "???",
           st.st_size,
           timebuf,
           filename);
}

int main(int argc, char *argv[]) {
    printf("argc = %d\n", argc);
    for (int i = 0; i < argc; ++i) {
        printf("argv[%d] = '%s'\n", i, argv[i]);
    }
    if (argc != 2) {
        fprintf(stderr, "Usage: %s <target-directory>\n", argv[0]);
        return EXIT_FAILURE;
    }

    const char *dirpath = argv[1];
    DIR *dir = opendir(dirpath);
    if (!dir) {
        perror("opendir");
        return EXIT_FAILURE;
    }

    struct dirent *entry;
    while ((entry = readdir(dir))) {
        if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
            continue;
        print_file_info(dirpath, entry->d_name);
    }

    closedir(dir);
    return EXIT_SUCCESS;
}

and I compile it with: gcc -Wall -Wextra -O2 -o fexls fexls.c.

I can confirm the ELF is indeed x86_64:

$ file /var/tmp/fexls 

/var/tmp/fexls: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4efe18c6f1e3fa1fffbc8347a43cb3c48d4be70d, for GNU/Linux 3.2.0, not stripped

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions