I am trying to modify and compile usbmouse.c
While compiling it is saying that the function usb_maxpackets requires 3 arguments but the code I copied from the torvald git is passing only 2 arguments to this function
the code can be found here:
https://github.com/torvalds/linux/blob/master/drivers/hid/usbhid/usbmouse.c
I am using debian gnu/linux 10with kernel version 4.19.94-ti-r42
The number of parameters of the usb_maxpacket() function was changed from 3 to 2 in Linux kernel 5.19 onwards. The third parameter indicated the direction of the pipe, but that information was redundant because the direction can be derived from the other two parameters.
For compatibility with the 4.19 kernel, the new function call maxp = usb_maxpacket(dev, pipe); needs to be changed to maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));. (usb_pipeout(pipe) is the pipe direction.)
To make the code compatible with old and new kernels, the code can be conditionally compiled according to the kernel version:
#include <linux/version.h>
/* In the function */
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,19,0)
maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
#else
maxp = usb_maxpacket(dev, pipe);
#endif
Alternatively, some compatibility code could be added before the function that calls usb_maxpacket:
#include <linux/version.h>
#include <linux/usb.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,19,0)
#undef usb_maxpacket
static inline u16 kcompat_usb_maxpacket(struct usb_device *udev, int pipe)
{
return usb_maxpacket(udev, pipe, usb_pipeout(pipe));
}
#define usb_maxpacket(udev, pipe) kcompat_usb_maxpacket(udev, pipe)
#endif
/* In the function */
maxp = usb_maxpacket(dev, pipe);
Related
Situation
I'm following this to better understand the Linux kernel.
There's an example on writing a custom /proc interface. Here's a snippet from the sample code linked above - custom-proc.c:
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/types.h>
#omitted
int create_new_proc_entry(void) {
int i;
char *DATA = "Hello People";
len = strlen(DATA);
msg = kmalloc((size_t) DATA_SIZE, GFP_KERNEL); // +1 for \0
if (msg != NULL) {
printk(KERN_INFO "Allocated memory for msg");
} else {
return -1;
}
strncpy(msg, DATA, len+1);
for (i=0; i < len +1 ; i++) {
printk(KERN_INFO "%c", msg[i]);
if (msg[i] == '\0') {
printk(KERN_INFO "YES");
}
}
proc = proc_create_data(MY_PROC_ENTRY, 0666, NULL, &proc_fops, msg);
if (proc) {
return 0;
}
return -1;
}
Since proc_create_data is not defined in the sample, I assume it must be defined somewhere in the kernel. I downloaded kernel 5x and search for all occurrence of proc_create_data in the kernel code and found that the only place where it was defined is in /include/linux/proc_fs.h:
#Omitted
#define proc_create_data(name, mode, parent, proc_ops, data) ({ NULL; })
#Omitted
extern struct proc_dir_entry *proc_create_data(const char *, umode_t,
struct proc_dir_entry *,
const struct proc_ops *, void *)
That's it.
I read from here that in C, macros will be textually expanded and one can use it like a function.
If I applied it here, then after all the expansion, I will have
/include/linux/proc_fs.h:
#omitted
extern struct proc_dir_entry ({ NULL; })
custom-proc.c
#omitted
proc = ({ NULL; })
Which doesn't seem right to me.
I'm not new to programming but not familiar with C. I read The C programming Language by Brian W. Kernighan and Dennis M. Ritchie but there explanation of macro is the same with the link above.
Question (Main Course :))
How do I understand the proc_create_data function as defined in the kernel?
I'm not looking for a very detail technical answer, just a way to interpret that code so I can follow along examples and be able to read more kernel code when needed by myself.
Side Dish
I heard somewhere that the kernel, despite being written in C, has some differences to "normal" C. I'm not sure if that's true or the extent of the discrepancy, however.
If this is a trick employed by "kernel version of the C language" it would be helpful if anyone knows a link to explanation of such tricks.
I've already looked around on following resources but couldn't find such info:
www.kernel.org
https://kernelnewbies.org/
Linux Kernel Development - Robert Love
Linux System Programming - Robert Love
It looks like this in proc_fs.h:
#ifdef CONFIG_PROC_FS
extern struct proc_dir_entry *proc_create_data(const char *, umode_t,
struct proc_dir_entry *,
const struct proc_ops *,
void *);
#else /* CONFIG_PROC_FS */
#define proc_create_data(name, mode, parent, proc_ops, data) ({NULL;})
#endif
If the user configured kernel without proc filesystem support, then the function call is expanded to a statement that just returns NULL. Note that ({...}) is a gcc extensions statement expression - do not use non-portable extensions in your code and prefer using static inline functions. The return value of a statement expression is the value of the last expression used - NULL in this case.
If CONFIG_PROC_FS is defined from user configuration, then proc/generic.c defines the function here just like any other function, and source files including proc_fs.h see the function declaration and do not see the macro.
I couldn't find the definition of proc_create_data function in Linux kernel
No worries - typing proc_create_data in elixir results in all 3 definitions mentioned in this answer. There are even multiple indexed kernel source code browsers on the net, with lxr as an example (but I see internal error right now on lxr). If not, you can index the source code on your computer with ctags or GNU GLOBAL and browse the code then.
How do I understand the proc_create_data function as defined in the kernel?
As any other function. No, the macro is not expanded with declaration - the compiler sees either the macro or the function declaration, depending if PROC_FS is enabled.
Side Dish I heard somewhere that the kernel, despite being written in C, has some differences to "normal" C.
Linux kernel specifically targets GNU GCC compiler, so it tends to use GNU C extensions, sometimes marked as GNU C.
I want to write a program using the new SCHED_DEADLINE scheduling policy available since Linux 3.14.
I start out with a simple program trying to use the sched_setattr function.
#include <sched.h>
int main(void)
{
// struct sched_attr attr;
// attr.size = sizeof(struct sched_attr);
// attr.sched_policy = SCHED_DEADLINE;
sched_setattr(0, (void*)0, 0);
return 0;
}
However when compiling I get the following error:
$gcc dead.c
dead.c: In function ‘main’:
dead.c:8:2: warning: implicit declaration of function ‘sched_setattr’ [-Wimplicit-function-declaration]
sched_setattr(0, (void*)0, 0);
^~~~~~~~~~~~~
/tmp/ccGxWxZE.o: In function `main':
dead.c:(.text+0x19): undefined reference to `sched_setattr'
collect2: error: ld returned 1 exit status
My system is running Ubuntu 16.10 Yakkety, with kernel 4.8.0-59-generic. The sched.h file included is found in /usr/include/sched.h and is provided by the package libc6-dev. This headerfile does not contain the function sched_setattr and friends that I am trying to use.
However the kernel (and kernel headers) I have installed comes with a sched.h header file containing the definitions I need. It is located at /usr/src/linux-headers-4.8.0-58/include/linux/sched.h, on my system.
So I naively think lets just build against the newer linux headers instead of the libc6-dev provided headers. My program will only run on this or newer kernels, but that is just fine.
I modify the first line to be: #include <linux/sched.h> and execute:
gcc -I/usr/src/linux-headers-$(uname -r)/include -I/usr/src/linux-headers-$(unam -r)/arch/x86/include dead.c
Now I am getting page after page of errors and warning. This does not seem the way to go.
What is the correct way to build a userspace program against a newer Linux headers than those that are provided by libc?
And subsequently how do I build the program above?
sched_setattr() is a syscall and doesn't seem to have one-to-one libc wrapper. You could do the wrapper yourself, something like this:
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <linux/sched.h>
#include <sys/syscall.h>
#include <sys/types.h>
struct sched_attr {
uint32_t size; /* Size of this structure */
uint32_t sched_policy; /* Policy (SCHED_*) */
uint64_t sched_flags; /* Flags */
int32_t sched_nice; /* Nice value (SCHED_OTHER, SCHED_BATCH) */
uint32_t sched_priority; /* Static priority (SCHED_FIFO, SCHED_RR) */
/* Remaining fields are for SCHED_DEADLINE */
uint64_t sched_runtime;
uint64_t sched_deadline;
uint64_t sched_period;
};
static int sched_setattr (pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
return syscall (SYS_sched_setattr, pid, attr, flags);
}
int main (int argc, char *argv[])
{
struct sched_attr attr;
int res;
memset (&attr, 0, sizeof (struct sched_attr));
attr.size = sizeof (struct sched_attr);
res = sched_setattr (getpid (), &attr, 0);
if (res < 0) {
perror ("sched_setattr");
return 1;
}
return 0;
}
Looking at the errors reported when trying to include kernel header files required to get the definition of struct sched_attr and reading the comments found by Googling "kernel headers in user space", I really can't suggest trying to include kernel header files just for this.
I'm trying to create another version of clone(2) syscall(in kernel space) to create a clone of a user process with some additional parameters.This system call will be doing exactly the same job as clone(2) but I want to pass one additional parameters to the kernel from user_space.However when I see the glibc's code
it seems that every parameter are not passed in the same order as user's call of the clone()
int clone(int (*fn)(void *), void *child_stack,
int flags, void *arg, ...
/* pid_t *ptid, void *newtls, pid_t *ctid */ );
rather some of them are handled by glibc's code itself.I searched the internet to learn how glib's clone() works but couldn't find any better documentation.
Can anyone please explain
How glibc handles the clone()?
And also all the parameters of syscall in kernel are not exactly the same as clone in glibc, so how is these variation handled?
How glibc handles the clone()?
Via arch-specific assembly wrappers. For i386, see sysdeps/unix/sysv/linux/i386/clone.S in the glibc sources; for x86-64, see sysdeps/unix/sysv/linux/x86-64/clone.S, and so on.
The normal syscall wrappers are not sufficient, because it is up to the userspace to switch stacks. The above assembly files have pretty informative comments as to what actually needs to be done in userspace in addition to the syscall.
All the parameters of syscall in kernel are not exactly the same as clone in glibc, so how is these variation handled?
C library functions that map to a syscall are wrapper functions.
Consider, for example, the POSIX.1 write() C library low-level I/O function, and the Linux write() syscall. The parameters are basically the same, as are the error conditions, but the error return values differ. The C library function returns -1 with errno set if an error occurs, whereas the Linux syscall returns negative error codes (which basically match errno values).
If you look at e.g. sysdeps/unix/sysv/linux/x86_64/sysdep.h, you can see that the basic syscall wrapper for Linux on x86-64 boils down to
# define INLINE_SYSCALL(name, nr, args...) \
({ \
unsigned long int resultvar = INTERNAL_SYSCALL (name, , nr, args); \
if (__glibc_unlikely (INTERNAL_SYSCALL_ERROR_P (resultvar, ))) \
{ \
__set_errno (INTERNAL_SYSCALL_ERRNO (resultvar, )); \
resultvar = (unsigned long int) -1; \
} \
(long int) resultvar; })
which just calls the actual syscall, then checks if the syscall return value indicated an error; and if it does, changes the result to -1 and sets errno accordingly. It's just funny-looking, because it relies on GCC extensions to make it behave as a single statement.
Let's say you added a new syscall to Linux, say
SYSCALL_DEFINE2(splork, unsigned long, arg1, void *, arg2);
and, for whatever reasons, you wish to expose it to userspace as
int splork(void *arg2, unsigned long arg1);
No problem! All you need is to provide a minimal header file,
#ifndef _SPLORK_H
#define _SPLORK_H
#define _GNU_SOURCE
#include <sys/syscall.h>
#include <errno.h>
#ifndef __NR_splork
#if defined(__x86_64__)
#define __NR_splork /* syscall number on x86-64 */
#else
#if defined(__i386)
#define __NR_splork /* syscall number on i386 */
#endif
#endif
#ifdef __NR_splork
#ifndef SYS_splork
#define SYS_splork __NR_splork
#endif
int splork(void *arg2, unsigned long arg1)
{
long retval;
retval = syscall(__NR_splork, (long)arg1, (void *)arg2);
if (retval < 0) {
/* Note: For backward compatibility, we might wish to use
*(__errno_location()) = -retval;
here. */
errno = -retval;
return -1;
} else
return (int)retval;
}
#else
#undef SYS_splork
int splork(void *arg2, unsigned long arg1)
{
/* Note: For backward compatibility, we might wish to use
*(__errno_location()) = ENOTSUP;
here. */
errno = ENOTSUP;
return -1;
}
#endif
#endif /* _SPLORK_H */
The SYS_splork and __NR_splork are preprocessor macros defining the syscall number for the new syscall. Since the syscall number is likely not (yet?) included in the official kernel sources and headers, the above header file explicitly declares it for each supported architecture. For architectures where it is not supported, the splork() function will always return -1 with errno == ENOTSUP.
Note, however, that Linux syscalls are limited to 6 parameters. If your kernel function needs more, you need to pack the parameters into a structure, pass the address of that structure to the kernel, and use copy_from_user() to copy the values to the same structure in-kernel.
In all Linux architectures, pointers and long are of the same size (int may be smaller than pointer), so I recommend you use either long or fixed-size types in such structures to pass data to/from the kernel.
It possible to use the clone syscall with almost no assembler.
The problem is not the stack switching which is done by the kernel as part of the system call but likely the glibc syscall() wrapper.
Using these bare-bones wrappers instead:
long _x64_syscall0(long n) {
long ret;
__asm__ __volatile__("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory");
return ret;
}
long _x64_syscall5(long n, long a1, long a2, long a3, long a4, long a5) {
long ret;
register long r10 __asm__("r10") = a4;
register long r8 __asm__("r8") = a5;
__asm__ __volatile__("syscall"
: "=a"(ret)
: "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10), "r"(r8)
: "rcx", "r11", "memory");
return ret;
}
A sketch of clone usage would look like:
int ret = _x64_syscall5(56 /* clone */ , CLONE_VM | SIGCHLD,
(long long)new_stack, 0, 0, 0);
if (ret == 0) {
// we are the child
ChildFunc();
_x64_syscall0(60 /* exit */ );
} else {
// we are the parent
}
I am working out of the 2nd edition of Jon Erickson's "Hacking: The Art of Exploitation" using a VM (virutalbox) to run the LiveCD it came with (Ubuntu 7.04). In section 0x281 "File Access", the author explains accessing files through file descriptors, as well as the open() close() read() and write() functions, using an example on pages 82-84.
The code for the simplenote.c is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
void usage(char *prog_name,char *filename){
printf("Usage: %s < data to add to %s>\n",prog_name,filename);
exit(0);
}
void fatal(char *);
void *ec_malloc(unsigned int );
int main(int argc,char *argv[]){
int fd; //file descriptor
char *buffer,*datafile;
buffer = (char *)ec_malloc(100);
datafile = (char *)ec_malloc(20);
strcpy(datafile,"/tmp/notes");
if(argc < 2)
usage(argv[0],datafile);
strcpy(buffer,argv[1]);
printf("[DEBUG] buffer # %p:\'%s'\n",buffer,buffer);
printf("[DEBUG] datafile # %p:\'%s'\n",datafile,datafile);
strncat(buffer,"\n",1);//Add a newline on the end.
fd = open(datafile,O_WRONLY|O_CREAT|O_APPEND,S_IRUSR|S_IWUSR);
if(fd == -1)
fatal("in main() while opening file");
printf("[DEBUG] file descriptor is %d\n",fd);
//Writing data
if(write(fd,buffer,strlen(buffer)) == -1)
fatal("in main() while writing buffer to file");
//Closing file
if(close(fd) == -1)
fatal("in main() while closing file");
printf("Note has been saved.\n");
free(buffer);
free(datafile);
}
//A function to display an error message and then exit
void fatal(char *message){
char error_message[100];
strcpy(error_message,"[!!]Fatal Error");
strncat(error_message,message,83);
perror(error_message);
exit(-1);
}
//An error-checked malloc() wrapper function
void *ec_malloc(unsigned int size){
void *ptr;
ptr = malloc(size);
if(ptr == NULL)
fatal("in ec_malloc() on memory allocation");
return ptr;
}
However, when I type the following instructions stated in the book into my terminal window, it returns the following error message:
reader#hacking:~/booksrc $ gcc -o simplenote simplenote.c
In file included from /usr/include/sys/stat.h:105, from simplenote.c:6:
/usr/include/bits/stat.h:70: error: field 'st_atim' has incomplete type
/usr/include/bits/stat.h:71: error: field 'st_mtim' has incomplete type
/usr/include/bits/stat.h:72: error: field 'st_ctim' has incomplete type
simplenote.c: In function 'main':
simplenote.c:35: error: 'O-WRONLY' undeclared (first use in this function)
simplenote.c:35: error: (Each undeclared identifier is reported only once
simplenote.c:35: error: for each function it appears in.)
simplenote.c:35: error: 'O_CREAT' undeclared (first use in this function)
simplenote.c:35: error: 'O_APPEND' undeclared (first use in this function)
Here is sys/stat.h line 105:
#include <bits/stat.h>
And here is bits/stat.h lines 63-83:
#ifdef __USE_MISC
/* Nanosecond resolution timestamps are stored in a format
equivalent to 'struct timespec'. This is the type used
whenever possible but the Unix namespace rules do not allow the
identifier 'timespec' to appear in the <sys/stat.h> header.
Therefore we have to handle the use of this header in strictly
standard-compliant sources special. */
struct timespec st_atim; /* Time of last access. */
struct timespec st_mtim; /* Time of last modification. */
struct timespec st_ctim; /* Time of last status change. */
# define st_atime st_atim.tv_sec /* Backward compatibility */
# define st_mtime st_mtim.tv_sec
# define st_ctime st_ctim.tv_sec
#else
__time_t st_atime; /* Time of last access. */
unsigned long int st_atimensec; /* Nscecs of last access. */
__time_t st_mtime; /* Time of last modification. */
unsigned long int st_mtimensec; /* Nsecs of last modification. */
__time_t st_ctime; /* Time of last status change. */
unsigned long int st_ctimensec; /* Nsecs of last status change. */
#endif
I suppose this might be of some use for the first set of issues:
C++ system file bits/stat.h suddenly breaks with "error: field ‘st_atim’ has incomplete type"
/usr/include/time.h
cat time.h
doesn't do anything in my terminal window.
And here is the simplenote.c main function lines 1-6, 34-35:
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
// Opening the file
fd = open(datafile, O_WRONLY|O_CREAT|O_APPEND, S_IRUSR|S_IWUSR);
I'm guessing the open function problems stem from fcntl.h ?
I seem to keep running into issues because of faulty code the author provided. I don't want to constantly depend on the stackoverflow community for help, so what suggestions do you have for a novice about examining and fixing these problems in the future?
Thanks.
Transferring a selection of comments into a semi-coherent answer.
You should probably explicitly enable POSIX definitions. Add -D_XOPEN_SOURCE=700 to the command line, or #define _XOPEN_SOURCE 700 before the first #include and see if that solves anything. You should not be running into the problem though; the header should be self-contained.
Oh, but Ubuntu 7.04 is archaic…you may need to use 600 instead of 700. When was it released (when was the book published)? If it was 2009 or before, you probably need the older version (600). It's still surprising that you're seeing the error. The command line you specify doesn't contain the options that normally cause trouble (-ansi -pedantic, for example, or -std=c99 -pedantic). You could try using -std=gnu99 too; it might work better.
You had a similar problem recently (gcc -o stdlib.h syntax error c Hacking the Art of Exploitation). Did you get that resolved? It sounds as if the compilation system on the Live CD is not self-coherent, or the way you're able to use it means that it is not behaving self-coherently. Are you sure the compilation system works? It appears to be semi-defunct. Is it using the wrong headers, somehow?
I was able to resolve the previous problem by inserting #include <stdint.h> before #include <stdlib.h>
I will try the -D_XOPEN_SOURCE=600 and get back to you. Something must be wrong with the compilation system.
Well, you might need to include <time.h> (or possibly <sys/time.h>) before <sys/stat.h>, but the <sys/stat.h> header is broken if that works. And the <stdlib.h> header is broken if you have to include <stdint.h> before including it. I suppose Ubuntu 7.04 could be so old that you're supposed to #include <sys/types.h> before many of these headers, but that's still not an excuse for <stdlib.h>; that should be self-contained. POSIX 1997 required #include <sys/types.h> before <sys/stat.h>; POSIX 2004 did not. And I don't think Ubuntu 7.04 is quite that old.
Note, though, that the st_atim member is new; it was added to POSIX 2008 (and hence is in POSIX 2013). It was just st_atime before (and st_atime is now a macro for st_atim.tv_sec).
Including the -D_XOPEN_SOURCE=600 dealt with the bits stat issue. Ubuntu 7.04 was released in 2007 and the 2nd edition of the book that I am using came out in 2008. Also, not sure if this is of use, but in another previous example that included both <stdio.h> and <string.h> (as opposed to only <stdio.h>), the code would run fine without any intervention.
Interesting…it is going to make life interesting for you, in a way that life should not need to be interesting. (Chinese curses like "May you live in interesting times" spring to mind.) Use the -DXOPEN_SOURCE=600 option in all your compilations and keep your fingers crossed; that may well resolve most of your problems. Consider using -std=gnu99 as well, or instead. With luck, either or both of those should get you around most problems.
In case someone else is having the same issue with this book, I downloaded the iso file from hacking-live-1.0.iso.
Created a bootable usb and all works just fine without corrupted headers or anything.
I have an application that has both two external kernel modules and a userspace daemon. I want to load the modules from the daemon code, written in C, at startup, and unload them on clean exit. Can I load them in a cleaner way than doing system("modprobe module"); and unload them using the corresponding rmmod?
init_module / remove_module minimal runnable example
Tested on a QEMU + Buildroot VM and Ubuntu 16.04 host with this simple parameter printer module .
We use the init_module / finit_module and remove_module Linux system calls.
The Linux kernel offers two system calls for module insertion:
init_module
finit_module
and:
man init_module
documents that:
The finit_module() system call is like init_module(), but reads the module to be loaded from the file descriptor fd. It is useful when the authenticity of a kernel module can be determined from its location in the filesystem; in cases where that is possible, the overhead of using cryptographically signed modules to determine the authenticity of a module can be avoided. The param_values argument is as for init_module().
finit is newer and was added only in v3.8. More rationale: https://lwn.net/Articles/519010/
glibc does not seem to provide a C wrapper for them, so we just create our own with syscall.
insmod.c
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define init_module(module_image, len, param_values) syscall(__NR_init_module, module_image, len, param_values)
#define finit_module(fd, param_values, flags) syscall(__NR_finit_module, fd, param_values, flags)
int main(int argc, char **argv) {
const char *params;
int fd, use_finit;
size_t image_size;
struct stat st;
void *image;
/* CLI handling. */
if (argc < 2) {
puts("Usage ./prog mymodule.ko [args="" [use_finit=0]");
return EXIT_FAILURE;
}
if (argc < 3) {
params = "";
} else {
params = argv[2];
}
if (argc < 4) {
use_finit = 0;
} else {
use_finit = (argv[3][0] != '0');
}
/* Action. */
fd = open(argv[1], O_RDONLY);
if (use_finit) {
puts("finit");
if (finit_module(fd, params, 0) != 0) {
perror("finit_module");
return EXIT_FAILURE;
}
close(fd);
} else {
puts("init");
fstat(fd, &st);
image_size = st.st_size;
image = malloc(image_size);
read(fd, image, image_size);
close(fd);
if (init_module(image, image_size, params) != 0) {
perror("init_module");
return EXIT_FAILURE;
}
free(image);
}
return EXIT_SUCCESS;
}
GitHub upstream.
rmmod.c
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#define delete_module(name, flags) syscall(__NR_delete_module, name, flags)
int main(int argc, char **argv) {
if (argc != 2) {
puts("Usage ./prog mymodule");
return EXIT_FAILURE;
}
if (delete_module(argv[1], O_NONBLOCK) != 0) {
perror("delete_module");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
GitHub upstream.
Busybox source interpretation
Busybox provides insmod, and since it is designed for minimalism, we can try to deduce how it is done from there.
On version 1.24.2, the entry point is at modutils/insmod.c function insmod_main.
The IF_FEATURE_2_4_MODULES is optional support for older Linux kernel 2.4 modules, so we can just ignore it for now.
That just forwards to modutils.c function bb_init_module.
bb_init_module attempts two things:
mmap the file to memory through try_to_mmap_module.
This always sets image_size to the size of the .ko file as a side effect.
if that fails, malloc the file to memory with xmalloc_open_zipped_read_close.
This function optionally unzips the file first if it is a zip, and just mallocs it otherwise.
I don't understand why this zipping business is done, since we can't even rely on it because the try_to_mmap_module does not seem to unzip things.
Finally comes the call:
init_module(image, image_size, options);
where image is the executable that was put into memory, and options are just "" if we call insmod file.elf without further arguments.
init_module is provided above by:
#ifdef __UCLIBC__
extern int init_module(void *module, unsigned long len, const char *options);
extern int delete_module(const char *module, unsigned int flags);
#else
# include <sys/syscall.h>
# define init_module(mod, len, opts) syscall(__NR_init_module, mod, len, opts)
# define delete_module(mod, flags) syscall(__NR_delete_module, mod, flags)
#endif
ulibc is an embedded libc implementation, and it seems to provide init_module.
If it is not present, I think glibc is assumed, but as man init_module says:
The init_module() system call is not supported by glibc. No declaration is provided in glibc headers, but, through a quirk of history, glibc does export an ABI for
this system call. Therefore, in order to employ this system call, it is sufficient to manually declare the interface in your code; alternatively, you can invoke
the system call using syscall(2).
BusyBox wisely follows that advice and uses syscall, which glibc provides, and which offers a C API for system calls.
insmod/rmmod use the functions init_module and delete_module to do this, which also have a man-page available. They both declare the functions as extern instead of including a header, but the man-page says they should be in <linux/module.h>.
I'd recommend against the use of system() in any daemon code that runs with root permissions as it's relatively easy to exploit from a security standpoint. modprobe and rmmod are, indeed, the right tools for the job. However, it'd be a bit cleaner and much more secure to use an explicit fork() + exec() to invoke them.
I'm not sure there's a cleaner way than system.
But for sure, if you want to load/unload the modules from your userspace daemon, then you force yourself to run the daemon as root*, which may not be considered as secure.
*: or you can add the explicit commands in the sudoers file, but this will be a nightmare to manage when deploying your application.
You can perform the same tasks that modprobe and Co. do, but I doubt that could be characterized as cleaner.