I’m trying to make a custom “loadable” builtin for Bash, but I keep getting a segmentation fault

I’m trying to make a “loadable” Bash builtin that can be enabled using the enable command. This builtin will use lseek to allow changing the byte offset of a file descriptor.

The end goal here is being able to rewind a file descriptor by 1 byte and to do so extremely fast (say, less than 100 μs). For this particular application the time it would take to call an external binary to implement this is too long, but I think/hope that a loadable builtin will be sufficiently fast.

I am able to create the shared dynamically loaded library file and can load it with enable -f ./lseek lseek, but when I try to use it I just get a segmentation fault (core dumped) error.

The lseek.c source code as well as the commands I’m using to compile it are shown in the script below.

NOTE: I have very little knowledge of C. The lseek.c source code is originally from ChatGPT, and then I tried my best to modify it using the example loadables and this guide, and sorta just tweaked stuff until it compiled and loaded. The compile commands are based off of what make runs to compile the other example loadables.

If anyone can see what the error is and tell me how to correct it I’d much appreciate it.


My env

  • OS: Fedora 40
  • Bash: 5.2.x

Build the loadable

git clone https://git.savannah.gnu.org/git/bash.git

cd bash

./configure

cd ./examples/loadables

cat<<'EOF' >lseek.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include "command.h"
#include "builtins.h"

int 
builtin_lseek (list)
WORD_LIST *list; {

    // Check for the right number of arguments
    if (list == NULL || list->word->word == NULL || list->next->word->word == NULL || list->next->next->word->word != NULL) {
        fprintf(stderr, "Usage: lseek <fd> <offset>n");
        return 1;
    }

    // Extract arguments from WORD_LIST
    char *fd_str = list->word->word;
    char *offset_str = list->next->word->word;

    int fd = atoi(fd_str);  // Convert the first argument to integer (file descriptor)
    off_t offset = atoll(offset_str);  // Convert the second argument to long long (offset)

    if (fd < 0) {
        fprintf(stderr, "Invalid file descriptor: %dn", fd);
        return 1;
    }

    off_t new_offset = lseek(fd, offset, SEEK_CUR);  // Use SEEK_CUR to move relative to current position
    if (new_offset == (off_t) -1) {
        perror("lseek");
        return 1;
    }

    printf("File descriptor %d moved to offset %lldn", fd, (long long)new_offset);
    return EXIT_SUCCESS;
}


char *lseek_doc[] =
{
  "Change file descriptor offset",
  "",
  "Changes the byte offset of the file descriptor relative to the current byte offset",
  "--> 1st input is file descriptor number (non-negative integer)",
  "--> 2nd input is relative byte offset (non-zero integer)",
  "",
  "Specifying a positive byte offset will advance the file descriptor",
  "Specifying a negative byte offset will rewind the file descriptor",
  (char *)NULL
};


struct builtin lseek_struct = {
    "lseek",   /* command name */
    builtin_lseek,      /* function implementing the builtin */
    BUILTIN_ENABLED,        /* initial flags for builtin */
    lseek_doc,              /* array of long documentation strings. */
    "lseek <fd> <rel_offset>",     /* usage synopsis; becomes short_doc */
    0                       /* reserved for internal use */
};
EOF

gcc -fPIC -DHAVE_CONFIG_H -DSHELL   -g -O2 -Wno-parentheses -Wno-format-security -I. -I.. -I../.. -I../../lib -I../../builtins -I. -I../../include -c -o lseek.o lseek.c
gcc -shared -Wl,-soname,lseek  -I /usr/include/bash -o lseek lseek.o 

Test the loadable

enable -f ./lseek lseek

(
  mapfile -t -n 11 -u $fd A
  lseek $fd 1; read -r -u $fd B
  printf '%sn' "${A[@]}"; echo; echo; echo $B
) {fd}<./lseek.c

Test result

Segmentation fault (core dumped)

3

This check is buggy:

if (list == NULL || list->word->word == NULL || list->next->word->word == NULL || list->next->next->word->word != NULL) {
    fprintf(stderr, "Usage: lseek <fd> <offset>n");
    return 1;
}

With lseek <fd> <offset>, only list (the fd) and list->next (the offset) are available so list->next->next would be NULL and list->next->next->word->word causes the segfault.

You can just change it to:

if (list == NULL || list->next == NULL) {
    fprintf(stderr, "Usage: lseek <fd> <offset>n");
    return 1;
}

I tried and the lseek loadable works fine with this change.


And it’s good practice to declare internal functions/vars as static:

static int builtin_lseek( ...

static char *lseek_doc[] = ...

3

I believe Ive figured out a working solution.


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include "command.h"
#include "builtins.h"
#include "shell.h"
#include "common.h"
#include "bashgetopt.h"
#include "xmalloc.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#ifndef errno
extern int errno;
#endif

// Function declaration for our builtin
static int lseek_main(int argc, char **argv);
int lseek_builtin(WORD_LIST *list);
extern char **make_builtin_argv();

// Metadata about the builtin
static char *lseek_doc[] = {
    "",
    "----------------------------------------------",
    "USAGE:    lseek <FD> <REL_OFFSET>",
    "",
    "Move the file descriptor <FD> by <REL_OFFSET>",
    "bytes relative to its current byte offset.",
    "",
    "positive <REL_OFFSET> advances the <FD>",
    "negative <REL_OFFSET> rewinds the <FD>",
    "----------------------------------------------",
    "",
    NULL
};

// Struct to register the builtin with bash
struct builtin lseek_struct = {
    "lseek",              // Name of the builtin
    lseek_builtin,        // Function to call
    BUILTIN_ENABLED,      // Default status
    lseek_doc,            // Documentation strings
    "lseek <FD> <REL_OFFSET>",  // Usage string
    0                     // Number of long options
};

// main function
static int lseek_main(int argc, char **argv) {
    // check for exactly 2 args passed to lseek
    if (argc != 3) {
        fprintf(stderr, "nIncorrect number of arguments.nUSAGE: lseek <FD> <REL_OFFSET>n");
        return 1;
    }

    // get + validate file descriptor
    int fd = atoi(argv[1]);
    if (fd == 0 && strcmp(argv[1], "0") != 0) {
        fprintf(stderr, "nERROR: Invalid file descriptor.n");
        return 1;
    }

     // get + validate (relative) offset
    errno = 0;
    off_t offset = atoll(argv[2]);
    if (errno == ERANGE) {
        fprintf(stderr, "nERROR: Offset out of range.n");
        return 1;
    }

    // call lseek to move fd byte offset 
    if (lseek(fd, offset, SEEK_CUR) == (off_t) -1) {
        fprintf(stderr, "nERROR: %sn", strerror(errno));
        return 1;
    }

    return 0;
}

// func to convert WORD_LIST to argc + argv 
// (this one is called by the builtin)
int lseek_builtin(WORD_LIST *list) {
    int c, r;
    char **v;

    v = make_builtin_argv(list, &c);
    r = lseek_main(c, v);
    xfree(v);

    return r;
}

2

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật