Changing file permissions in kernel - c

I am writing kernel module(C in Linux) and I want to change the permission of the other files in it.
any solution?
since I am in kernel I can't use chmod syscall and ...
thanks for your help
This is my Makefile:
> obj-m += ca.o
>
> all:
> make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
>
> clean:
> make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
And this is my Code:
> #include <linux/string.h>
> #include <linux/mm.h>
> /* Snip, tons of includes (all of them :))*/
> #include <linux/delay.h> .... int procfile_write(struct file *file,
> const char *buffer, unsigned long
> count,
> void *data) { ... sys_chmod(path, per); ... } ...
When Making it gives a warning:
WARNING: "sys_chmod" [file] undefiened
and when loading the module with "sudo insmod" it gives this error:
Unknown sybol in module
it seems that this error happens especialy in kernel modules. any idea? again thanks!

Welcome to stackoverflow! IIRC you want sys_chmod()
From the Linux Kernel Mailing List
On Thu, Feb 20, 2003 at 11:10:27PM +0100, Andrea Arcangeli wrote:
On Thu, Feb 20, 2003 at 12:40:43PM -0500, Jeff Garzik wrote:
On Thu, Feb 20, 2003 at 11:04:37PM +0530, Prasad wrote:
Is there a way using which i could invoke a syscall in the kernel
space? The syscall is to be run disguised as another process. The actual
Call sys_whatever(). Look at the kernel code for examples.
The kernel already does this in
various places. sys_read, sys_write,
open_filp, sys_close, and other
functions are safe to call from kernel
code -- though this is discouraged.
init/do_mounts.c is a particularly
annoying case, and is a big reason why
klibc needs to be merged. syscalls
should be made from userspace, not the
kernel.
People are starting to worry, as this isn't the kind of thing you might do in the kernel (unless you are use you know what you are doing). If you just want to change permissions on a certain event, do it from userspace with inotify or similar.
Disclaimer aside:
Here is some code I found in another kernel module, which uses the sys_* calls:
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
#include <linux/string.h>
#include <linux/syscalls.h>
/* Snip */
int openflags = O_WRONLY|O_CREAT;
if (ml != 1)
openflags |= O_TRUNC;
wfd = sys_open(collected, openflags, mode);
if (wfd >= 0) {
sys_fchown(wfd, uid, gid);
sys_fchmod(wfd, mode);
state = CopyFile;
}
Also found:
asmlinkage long sys_rename(const char __user *oldname, const char __user *newname);
asmlinkage long sys_chmod(const char __user *filename, mode_t mode);
asmlinkage long sys_fchmod(unsigned int fd, mode_t mode);
in include/linux/syscalls.h
Mind you, it has been a while since I did any kernel stuff. Check that this is the appropriate interface for chmod stuff and that you arn't shortcutting any other call that might implement security hooks, for example.
Also, This link contains information on syscalls and their symbols. Also Here is a quick-reference of user-space API system calls and where they are implemented in the kernel.

The syscalls are not exported symbols. You need to do a little bit of hacking if you want them.
you want to get your fingers on sys_call_table. It contains a pointer to every syscall.
Look at arch/x86/kernel/syscall_table_32.S or arch/i386/kernel/entry.S on older kernels.
You can grep sys_call_table /usr/src/linux/System.map (or /proc/kallsyms if the symbols are exported) to find the base address of this table.
You can have this address as a parameter for your module (converting an hex string to a pointer will be needed).
You'll be able to call the right syscall with the offset defined in arch/x86/include/asm/unistd_32.h (or include/asm-i386/unistd.h on older kernels).
You get something like: #define __NR_chmod 15
Macros are helpful:
#define DO_SYSCALL_2(sc, t1, a1, t2, a2) \
(((asmlinkage long (*)(t1, t2)) sys_call_table[__NR_##sc]) (a1, a2));
#define USER_SYSCALL_2(sc, t1, a1, t2, a2) \
static inline asmlinkage long syscall_##sc(t1 a1, t2 a2) \
{ return DO_SYSCALL_2(sc, t1, a1, t2, a2) }
USER_SYSCALL_2(chmod, const char __user *, filename, mode_t, mode);
int my_code(void) { return syscall_chmod(arg1, arg2); }
Also, if you are passing kernel buffer (for filename for examples) that are supposed to be user buffers, don't forget to change the data segment:
mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS);
ret = syscall_XXX(...);
set_fs(oldfd);

Related

Where do Linux shells look for interpreters for ELF binaries? [duplicate]

So everyone probably knows that glibc's /lib/libc.so.6 can be executed in the shell like a normal executable in which cases it prints its version information and exits. This is done via defining an entry point in the .so. For some cases it could be interesting to use this for other projects too. Unfortunately, the low-level entry point you can set by ld's -e option is a bit too low-level: the dynamic loader is not available so you cannot call any proper library functions. glibc for this reason implements the write() system call via a naked system call in this entry point.
My question now is, can anyone think of a nice way how one could bootstrap a full dynamic linker from that entry point so that one could access functions from other .so's?
Update 2: see Andrew G Morgan's slightly more complicated solution which does work for any GLIBC (that solution is also used in libc.so.6 itself (since forever), which is why you can run it as ./libc.so.6 (it prints version info when invoked that way)).
Update 1: this no longer works with newer GLIBC versions:
./a.out: error while loading shared libraries: ./pie.so: cannot dynamically load position-independent executable
Original answer from 2009:
Building your shared library with -pie option appears to give you everything you want:
/* pie.c */
#include <stdio.h>
int foo()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return 42;
}
int main()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return foo();
}
/* main.c */
#include <stdio.h>
extern int foo(void);
int main()
{
printf("in %s %s:%d\n", __func__, __FILE__, __LINE__);
return foo();
}
$ gcc -fPIC -pie -o pie.so pie.c -Wl,-E
$ gcc main.c ./pie.so
$ ./pie.so
in main pie.c:9
in foo pie.c:4
$ ./a.out
in main main.c:6
in foo pie.c:4
$
P.S. glibc implements write(3) via system call because it doesn't have anywhere else to call (it is the lowest level already). This has nothing to do with being able to execute libc.so.6.
I have been looking to add support for this to pam_cap.so, and found this question. As #EmployedRussian notes in a follow-up to their own post, the accepted answer stopped working at some point. It took a while to figure out how to make this work again, so here is a worked example.
This worked example involves 5 files to show how things work with some corresponding tests.
First, consider this trivial program (call it empty.c):
int main(int argc, char **argv) { return 0; }
Compiling it, we can see how it resolves the dynamic symbols on my system as follows:
$ gcc -o empty empty.c
$ objcopy --dump-section .interp=/dev/stdout empty ; echo
/lib64/ld-linux-x86-64.so.2
$ DL_LOADER=/lib64/ld-linux-x86-64.so.2
That last line sets a shell variable for use later.
Here are the two files that build my example shared library:
/* multi.h */
void multi_main(void);
void multi(const char *caller);
and
/* multi.c */
#include <stdio.h>
#include <stdlib.h>
#include "multi.h"
void multi(const char *caller) {
printf("called from %s\n", caller);
}
__attribute__((force_align_arg_pointer))
void multi_main(void) {
multi(__FILE__);
exit(42);
}
const char dl_loader[] __attribute__((section(".interp"))) =
DL_LOADER ;
(Update 2021-11-13: The forced alignment is to help __i386__ code be SSE compatible - without it we get hard to debug glibc SIGSEGV crashes.)
We can compile and run it as follows:
$ gcc -fPIC -shared -o multi.so -DDL_LOADER="\"${DL_LOADER}\"" multi.c -Wl,-e,multi_main
$ ./multi.so
called from multi.c
$ echo $?
42
So, this is a .so that can be executed as a stand alone binary. Next, we validate that it can be loaded as shared object.
/* opener.c */
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
void *handle = dlopen("./multi.so", RTLD_NOW);
if (handle == NULL) {
perror("no multi.so load");
exit(1);
}
void (*multi)(const char *) = dlsym(handle, "multi");
multi(__FILE__);
}
That is we dynamically load the shared-object and run a function from it:
$ gcc -o opener opener.c -ldl
$ ./opener
called from opener.c
Finally, we link against this shared object:
/* main.c */
#include "multi.h"
int main(int argc, char **argv) {
multi(__FILE__);
}
Where we compile and run it as follows:
$ gcc main.c -o main multi.so
$ LD_LIBRARY_PATH=./ ./main
called from main.c
(Note, because multi.so isn't in a standard system library location, we need to override where the runtime looks for the shared object file with the LD_LIBRARY_PATH environment variable.)
I suppose you'd have your ld -e point to an entry point which would then use the dlopen() family of functions to find and bootstrap the rest of the dynamic linker. Of course you'd have to ensure that dlopen() itself was either statically linked or you might have to implement enough of your own linker stub to get at it (using system call interfaces such as mmap() just as libc itself is doing.
None of that sounds "nice" to me. In fact just the thought of reading the glibc sources (and the ld-linux source code, as one example) enough to assess the size of the job sounds pretty hoary to me. It might also be a portability nightmare. There may be major differences between how Linux implements ld-linux and how the linkages are done under OpenSolaris, FreeBSD, and so on. (I don't know).

What prevents from copy_to_user to succeed on kmap'ed pages from a non-current task?

This is a new and modified question for: What is needed to be able to access a user pointer from kernel-space as I lost access to my account.
I want to write to start_arg. start_arg is a pointer to a user-space process argv[0], i.e int main(int argc, char **argv). I start by associated a process id to struct task_struct, pull out the start_arg and arg_end from task_struct->mm->arg_(end/start).
After that, I pin the user space pages of the process (starting from arg_start) to kernel address space, map them, and then try to write to the virtual address I have from kmap. Is that the correct way to accomplish my task? If so, why does copy_to_user always fail?
EDIT:
Looks like kmap returns a kernel space address of the page, so the problem has been solved, however I can't try the kernel code because looks like get_user_pages always fail as well with -14 (Bad Address)
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/highmem.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/pid.h>
#include <linux/mm.h>
static int PID;
module_param(PID, int, 0);
#define NR_PAGES (1)
#define BUF_SIZE (256) /* IDK why */
/* A struct to store the start address of the process args, and the end address */
struct args {
u64 start_addr;
u64 end_addr;
};
/*
* Overwrite the argument address with our local buffer \
* The argument address will most probably contain the first variable in argv \
* That is, possibly, we overwrite argv[0]
*/
int exploit(struct mm_struct *mm, struct args args)
{
long res;
struct page *p;
void __user *vaddr;
const char *buf = "Hello!";
/* Make sure we can write stuff */
if (!(mm->mmap->vm_flags & (VM_WRITE | VM_MAYWRITE)))
return -EPERM;
/* Pin pages in memory */
mmap_read_lock(mm);
res = get_user_pages(args.start_addr, NR_PAGES, FOLL_WRITE, &p, NULL);
if (res < 0)
goto err;
mmap_read_unlock(mm);
vaddr = kmap(p);
/* TODO: FIX copy_to_user always fails */
if (copy_to_user(vaddr, buf, strlen(buf) + 1))
goto err_copy;
kunmap(p);
set_page_dirty_lock(p);
put_page(p);
pr_info("[Y] argv[0] has been successfully overwritten!\n");
return 0;
err:
pr_err("[X] FAILURE. ERROR CODE: %ld\n", res);
return -EFAULT;
err_copy:
kunmap(p);
put_page(p);
pr_err("[X] FAILURE. ERROR CODE: %ld\n", res);
return -EFAULT;
}
/* Copy the user-space program arguments addresses into our variables */
void psmem(struct mm_struct *mm, u64 *arg_start, u64 *arg_end)
{
spin_lock(&mm->arg_lock);
*arg_start = mm->arg_start;
*arg_end = mm->arg_end;
spin_unlock(&mm->arg_lock);
}
int view_values(struct task_struct *ts, struct mm_struct *mm, struct args args)
{
char *kbuf;
size_t bytes_read;
unsigned long length = args.end_addr - args.start_addr;
if (!(kbuf = kmalloc(BUF_SIZE, GFP_KERNEL)))
return -EFAULT;
/* Read length bytes starting from args.start_addr */
bytes_read = access_process_vm(ts, args.start_addr, kbuf, length, FOLL_FORCE);
pr_info("[I] Number of bytes read: %zu of %ld; Data: '%s'\n",
bytes_read, length, kbuf);
kfree(kbuf);
return 0;
}
static int __init init_mod(void)
{
struct args args;
struct mm_struct *pid_mm;
struct task_struct *pid_task_struct;
if (PID <= 0 || PID > PID_MAX_LIMIT)
return -EINVAL;
pid_task_struct = pid_task(find_vpid(PID), PIDTYPE_PID);
if (!pid_task_struct)
return -EINVAL;
pid_mm = pid_task_struct->mm;
psmem(pid_mm, &args.start_addr, &args.end_addr);
view_values(pid_task_struct, pid_mm, args);
if (exploit(pid_mm, args) < 0)
return -EFAULT;
pr_info("%s: Successfully written to process' memory!\n", THIS_MODULE->name);
return 0;
}
static void __exit exit_mod(void)
{
}
MODULE_LICENSE("GPL");
module_init(init_mod);
module_exit(exit_mod);
obj-m := mymodule.o
CFLAGS_EXTRA += -pedantic-errors -Wall -Werror -Wextra -g -DDEBUG
all: build
build:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
copy_to_user expects a user-space virtual address. I think it would check to make sure the high bit isn't set (or a compare on 32-bit with a 3:1 split), so user-space can't pass in a wild pointer and make the kernel overwrite its own memory.
Your destination is not a user-space virtual address, so copy_to_user will correctly refuse to write to it. It's a kernel virtual address that happens to point at some physical pages which are also mapped by a user-space process.
If you've already pinned the memory and found a kernel virtual address to access the pages through, I think you can just memcpy. But I'm much less confident of that; I understand enough general stuff about how kernels work (and Linux specifically) to read kernel code and have things make sense, but I don't know enough to say for sure you're not leaving out anything important.
(And I haven't even read your code; your text description was sufficient for me to be pretty sure what the problem is.)

How do I find the RAM usage of a process using its ID?

I am really new to this Kernel stuff. I went here and I found this code that outputs process information like its ID.
main.c:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched/signal.h>
#include <linux/sched.h>
struct task_struct *task; /* Structure defined in sched.h for tasks/processes */
struct task_struct *task_child; /* Structure needed to iterate through task children */
struct list_head *list; /* Structure needed to iterate through the list in each task->children struct */
int iterate_init(void) /* Init Module */
{
printk(KERN_INFO "%s","LOADING MODULE\n"); /* good practice to log when loading/removing modules */
for_each_process( task ){ /* for_each_process() MACRO for iterating through each task in the os located in linux\sched\signal.h */
printk(KERN_INFO "\nPARENT PID: %d PROCESS: %s STATE: %ld",task->pid, task->comm, task->state);/* log parent id/executable name/state */
list_for_each(list, &task->children){ /* list_for_each MACRO to iterate through task->children */
task_child = list_entry( list, struct task_struct, sibling ); /* using list_entry to declare all vars in task_child struct */
printk(KERN_INFO "\nCHILD OF %s[%d] PID: %d PROCESS: %s STATE: %ld",task->comm, task->pid, /* log child of and child pid/name/state */
task_child->pid, task_child->comm, task_child->state);
}
printk("-----------------------------------------------------"); /*for aesthetics*/
}
return 0;
} /* End of Init Module */
void cleanup_exit(void) /* Exit Module */
{
printk(KERN_INFO "%s","REMOVING MODULE\n");
} /* End of Exit Module */
module_init(iterate_init); /* Load Module MACRO */
module_exit(cleanup_exit); /* Remove Module MACRO */
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("ITERATE THROUGH ALL PROCESSES/CHILD PROCESSES IN THE OS");
MODULE_AUTHOR("Laerehte");
Makefile:
obj-m += main.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
Executed with ./ins (with chmod +x)
Code:
sudo insmod main.ko
sudo rmmod main
sudo dmesg -c
I looked up how to find how much memory a process uses, and I found this question: Memory usage of current process in C.
Correct me if I'm wrong here, but I'm thinking that you can read the current RAM usage of these processes by looking in /proc/[process_id]/status. I found out from another place(forgot where) that within this file, there is something called VmRSS that would hold the current RAM usage of the process.
You can apparently use:
ssize_t kernel_read(struct file *file, void *buf, size_t count, loff_t *pos);
to read a file, but I have not been able to modify main.c with this successfully. I need to find the size of the file, but I also have not been able to use vfs_stat correctly. When I just try some constant integer, I get all 0s in the buffer anyway. I don't know how to use these functions properly. I'm trying to modify main.c so that I will see the RAM usage of these processes along with the other information. Much of the information I found was outdated. Can anyone help?
While you can technically open and read files from kernel space, it's usually a bad idea for multiple reasons. Whatever information is provided under /proc is already available to the kernel, you just need to figure out where it is and how to obtain it, and you will be able to do everything in your module without reading any file.
You are interested in knowing how much RAM is a particular process using given its PID, and you are correct that this statistic is available in /proc/<PID>/status: in fact, it is labeled as "VmRSS" which means "virtual memory resident set size". If we take a look at the kernel source code, we can see exactly how that number is calculated:
The kernel function called when reading /proc/<PID>/status is proc_pid_status() in fs/proc/array.c, which then calls (among the other things) task_mem().
// ...
anon = get_mm_counter(mm, MM_ANONPAGES);
file = get_mm_counter(mm, MM_FILEPAGES);
shmem = get_mm_counter(mm, MM_SHMEMPAGES);
// ...
hiwater_rss = total_rss = anon + file + shmem;
// ...
SEQ_PUT_DEC(" kB\nVmHWM:\t", hiwater_rss);
SEQ_PUT_DEC(" kB\nVmRSS:\t", total_rss); // <===== here it is
SEQ_PUT_DEC(" kB\nRssAnon:\t", anon);
// ...
You can therefore obtain this information in the same way. First get ahold of the task_struct of the process you want to check, then inspect the ->mm field like get_mm_counter() does, or simply using get_mm_rss() from include/linux/mm.h, which does exactly what we want.
Note that:
The value obtained from get_mm_counter() or get_mm_rss() is the number of pages, you will have to multiply this by the page size (simply shift left by PAGE_SHIFT).
You will only be able to do this if your kernel was compiled with MMU support (CONFIG_MMU=y), you can check this in your module code with a simple #ifdef or #ifndef.
Here's a working example module to do this for all processes:
// SPDX-License-Identifier: GPL-3.0
#include <linux/kernel.h> // printk(), pr_*()
#include <linux/module.h> // THIS_MODULE, MODULE_VERSION, ...
#include <linux/init.h> // module_{init,exit}
#include <linux/sched/task.h> // struct task_struct, {get,put}_task_struct()
#include <linux/sched/signal.h> // for_each_process()
#include <linux/sched/mm.h> // get_task_mm(), mmput()
#include <linux/mm.h> // get_mm_rss()
/* Tested on kernel 5.6, qemu-system-aarch64
* Usage: sudo insmod task_rss
* sudo modprobe task_rss
*/
#ifdef pr_fmt
#undef pr_fmt
#endif
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
static int __init modinit(void)
{
struct task_struct *tsk;
struct mm_struct *mm;
unsigned long rss;
char comm[TASK_COMM_LEN];
#ifndef CONFIG_MMU
pr_err("No MMU, cannot calculate RSS.\n");
return -EINVAL;
#endif
for_each_process(tsk) {
get_task_struct(tsk);
get_task_comm(comm, tsk);
mm = get_task_mm(tsk);
// https://www.kernel.org/doc/Documentation/vm/active_mm.rst
if (mm) {
rss = get_mm_rss(mm) << PAGE_SHIFT;
mmput(mm);
pr_info("PID %d (\"%s\") VmRSS = %lu bytes\n", tsk->pid, comm, rss);
} else {
pr_info("PID %d (\"%s\") is an anonymous process\n", tsk->pid, comm);
}
put_task_struct(tsk);
}
return 0;
}
static void __exit modexit(void)
{
// This function is only needed to be able to unload the module.
}
module_init(modinit);
module_exit(modexit);
MODULE_VERSION("0.1");
MODULE_DESCRIPTION("Silly test module to calculare task RSS of all running tasks.");
MODULE_AUTHOR("Marco Bonelli");
MODULE_LICENSE("GPL");
Output on my machine (qemu-system-aarch64):
/ # insmod task_rss.ko
[ 7.306284] task_rss: loading out-of-tree module taints kernel.
[ 7.312912] task_rss: PID 1 ("init") VmRSS = 4096 bytes
[ 7.313295] task_rss: PID 2 ("kthreadd") is an anonymous process
[ 7.314039] task_rss: PID 3 ("rcu_gp") is an anonymous process
...
[ 7.330169] task_rss: PID 146 ("sh") VmRSS = 1363968 bytes
[ 7.330554] task_rss: PID 147 ("insmod") VmRSS = 1339392 bytes

avoid LD_PRELOAD: Wrap library and provide functionality requested from libc

I have a shared library, say somelib.so, which uses ioctl from libc (according to objdump).
My goal is to write a new library that wraps around somelib.so and provides a custom ioctl. I want to avoid preloading a library to ensure that only the calls in somelib.so use the custom ioctl.
Here is my current snippet:
typedef int (*entryfunctionFromSomelib_t) (int par, int opt);
typedef int (*ioctl_t) (int fd, int request, void *data);
ioctl_t real_ioctl = NULL;
int ioctl(int fd, int request, void *data )
{
fprintf( stderr, "trying to wrap ioctl\n" );
void *handle = dlopen( "libc.so.6", RTLD_NOW );
if (!handle)
fprintf( stderr, "Error loading libc.so.6: %s\n", strerror(errno) );
real_ioctl = (ioctl_t) dlsym( handle, "ioctl" );
return real_ioctl( fd, request, data);
}
int entryfunctionFromSomelib( int par, int opt ) {
void *handle = dlopen( "/.../somelib.so", RTLD_NOW );
if (!handle)
fprintf( stderr, "Error loading somelib.so: %s\n", strerror(errno) );
real_entryfunctionFromSomelib = entryfunctionFromSomelib_t dlsym( handle, "entryfunctionFromSomelib" );
return real_entryfunctionFromSomelib( par, opt );
}
However, it does not in work in the sense that the calls to ioctl form somelib.so are not redirected to my custom ioctl implementation. How can I enforce that the wrapped somelib.so does so?
======================
Additional information added after #Nominal Animal post:
Here some information from mylib.so (somelib.so after edit) obtained via readelf -s | grep functionname:
246: 0000000000000000 121 FUNC GLOBAL DEFAULT UND dlsym#GLIBC_2.2.5 (11)
42427: 0000000000000000 121 FUNC GLOBAL DEFAULT UND dlsym##GLIBC_2.2.5
184: 0000000000000000 37 FUNC GLOBAL DEFAULT UND ioctl#GLIBC_2.2.5 (6)
42364: 0000000000000000 37 FUNC GLOBAL DEFAULT UND ioctl##GLIBC_2.2.5
After 'patching' mylib.so it also shows the new function as:
184: 0000000000000000 37 FUNC GLOBAL DEFAULT UND iqct1#GLIBC_2.2.5 (6)
I 'versioned' and exported the symbols from my wrap_mylib library for which readelf now shows:
25: 0000000000000d15 344 FUNC GLOBAL DEFAULT 12 iqct1#GLIBC_2.2.5
63: 0000000000000d15 344 FUNC GLOBAL DEFAULT 12 iqct1#GLIBC_2.2.5
However, when I try to dlopen wrap_mylib I get the following error:
symbol iqct1, version GLIBC_2.2.5 not defined in file libc.so.6 with link time reference
Is that maybe because mylib.so tries to dlsym iqct1 from libc.so.6 ?
If binutils' objcopy could modify dynamic symbols, and the mylib.so is an ELF dynamic library, we could use
mv mylib.so old.mylib.so
objcopy --redefine-sym ioctl=mylib_ioctl old.mylib.so mylib.so
to rename the symbol name in the library from ioctl to mylib_ioctl, so we could implement
int mylib_ioctl(int fd, int request, void *data);
in another library or object linked to the final binaries.
Unfortunately, this feature is not implemented (as of early 2017 at least).
We can solve this using an ugly hack, if the replacement symbol name is exactly the same length as the original name. The symbol name is a string (both preceded and followed by a nul byte) in the ELF file, so we can just replace it using e.g. GNU sed:
LANG=C LC_ALL=C sed -e 's|\x00ioctl\x00|\x00iqct1\x00|g' old.mylib.so > mylib.so
This replaces the name from ioctl() to iqct1(). It is obviously less than optimal, but it seems the simplest option here.
If you find you need to add version information to the iqct1() function you implement, with GCC you can simply add a line similar to
__asm__(".symver iqct1,iqct1#GLIBC_2.2.5");
where the version follows the # character.
Here is a practical example, showing how I tested this in practice.
First, let's create mylib.c, representing the sources for mylib.c (that the OP does not have -- otherwise just altering the sources and recompiling the library would solve the issue):
#include <unistd.h>
#include <errno.h>
int myfunc(const char *message)
{
int retval = 0;
if (message) {
const char *end = message;
int saved_errno;
ssize_t n;
while (*end)
end++;
saved_errno = errno;
while (message < end) {
n = write(STDERR_FILENO, message, (size_t)(end - message));
if (n > 0)
message += n;
else {
if (n == -1)
retval = errno;
else
retval = EIO;
break;
}
}
errno = saved_errno;
}
return retval;
}
The only function exported is myfunc(message), as declared in mylib.h:
#ifndef MYLIB_H
#define MYLIB_H
int myfunc(const char *message);
#endif /* MYLIB_H */
Let's compile the mylib.c into a dynamic shared library, mylib.so:
gcc -Wall -O2 -fPIC -shared mylib.c -Wl,-soname,libmylib.so -o mylib.so
Instead of write() from the C library (it's a POSIX function just like ioctl(), not a standard C one), we wish to use mywrt() of our own design in our own program. The above command saves the original library as mylib.so (while naming it internally as libmylib.so), so we can use
sed -e 's|\x00write\x00|\x00mywrt\x00|g' mylib.so > libmylib.so
to alter the symbol name, saving the modified library as libmylib.so.
Next, we need a test executable, that provides the ssize_t mywrt(int fd, const void *buf, size_t count); function (the prototype being the same as the write(2) function it replaces. test.c:
#include <stdlib.h>
#include <stdio.h>
#include "mylib.h"
ssize_t mywrt(int fd, const void *buffer, size_t bytes)
{
printf("write(%d, %p, %zu);\n", fd, buffer, bytes);
return bytes;
}
__asm__(".symver mywrt,mywrt#GLIBC_2.2.5");
int main(void)
{
myfunc("Hello, world!\n");
return EXIT_SUCCESS;
}
The .symver line specifies version GLIBC_2.2.5 for mywrt.
The version depends on the C library used. In this case, I ran objdump -T $(locate libc.so) 2>/dev/null | grep -e ' write$', which gave me
00000000000f66d0 w DF .text 000000000000005a GLIBC_2.2.5 write
the second to last field of which is the version needed.
Because the mywrt symbol needs to be exported for the dynamic library to use, I created test.syms:
{
mywrt;
};
To compile the test executable, I used
gcc -Wall -O2 test.c -Wl,-dynamic-list,test.syms -L. -lmylib -o test
Because libmylib.so is in the current working directory, we need to add current directory to the dynamic library search path:
export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH
Then, we can run our test binary:
./test
It will output something like
write(2, 0xADDRESS, 14);
because that's what the mywrt() function does. If we want to check the unmodified output, we can run mv -f mylib.so libmylib.so and rerun ./test, which will then output just
Hello, world!
This shows that this approach, although depending on very crude binary modification of the shared library file (using sed -- but only because objcopy does not (yet) support --redefine-sym on dynamic symbols), should work just fine in practice.
This is also a perfect example of how open source is superior to proprietary libraries: the amount of effort already spent in trying to fix this minor issue is at least an order of magnitude higher than it would have been to rename the ioctl call in the library sources to e.g. mylib_ioctl(), and recompile it.
Interposing dlsym() (from <dlfcn.h>, as standardized in POSIX.1-2001) in the final binary seems necessary in OP's case.
Let's assume the original dynamic library is modified using
sed -e 's|\x00ioctl\x00|\x00iqct1\x00|g;
s|\x00dlsym\x00|\x00d15ym\x00|g;' mylib.so > libmylib.so
and we implement the two custom functions as something like
int iqct1(int fd, unsigned long request, void *data)
{
/* For OP to implement! */
}
__asm__(".symver iqct1,iqct1#GLIBC_2.2.5");
void *d15ym(void *handle, const char *symbol)
{
if (!strcmp(symbol, "ioctl"))
return iqct1;
else
if (!strcmp(symbol, "dlsym"))
return d15ym;
else
return dlsym(handle, symbol);
}
__asm__(".symver d15ym,d15ym#GLIBC_2.2.5");
Do check the versions correspond to the C library you use. The corresponding .syms file for the above would contain just
{
i1ct1;
d15ym;
};
otherwise the implementation should be as in the practical example shown earlier in this answer.
Because the actual prototype for ioctl() is int ioctl(int, unsigned long, ...);, there are no quarantees that this will work for all general uses of ioctl(). In Linux, the second parameter is of type unsigned long, and the third parameter is either a pointer or a long or unsigned long -- in all Linux architectures pointers and longs/unsigned longs have the same size --, so it should work, unless the driver implementing the ioctl() is also closed, in which case you are simply hosed, and limited to either hoping this works, or switching to other hardware with proper Linux support and open-source drivers.
The above special-cases both original symbols, and hard-wires them to the replaced functions. (I call these replaced instead of interposed symbols, because we really do replace the symbols the mylib.so calls with these ones, rather than interpose calls to ioctl() and dlsym().)
It is a rather brutish approach, but aside from using sed due to the lack of dynamic symbol redefinition support in objcopy, it is quite robust and clear as to what is done and what actually happens.

Get Linux system information in C

I have to check Linux system information. I can execute system commands in C, but doing so I create a new process for every one, which is pretty expensive. I was wondering if there is a way to obtain system information without being forced to execute a shell command. I've been looking around for a while and I found nothing. Actually, I'm not even sure if it's more convenient to execute commands via Bash calling them from my C program or find a way to accomplish the tasks using only C.
Linux exposes a lot of information under /proc. You can read the data from there. For example, fopen the file at /proc/cpuinfo and read its contents.
A presumably less known (and more complicated) way to do that, is that you can also use the api interface to sysctl. To use it under Linux, you need to #include <unistd.h>, #include <linux/sysctl.h>. A code example of that is available in the man page:
#define _GNU_SOURCE
#include <unistd.h>
#include <sys/syscall.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <linux/sysctl.h>
int _sysctl(struct __sysctl_args *args );
#define OSNAMESZ 100
int
main(void)
{
struct __sysctl_args args;
char osname[OSNAMESZ];
size_t osnamelth;
int name[] = { CTL_KERN, KERN_OSTYPE };
memset(&args, 0, sizeof(struct __sysctl_args));
args.name = name;
args.nlen = sizeof(name)/sizeof(name[0]);
args.oldval = osname;
args.oldlenp = &osnamelth;
osnamelth = sizeof(osname);
if (syscall(SYS__sysctl, &args) == -1) {
perror("_sysctl");
exit(EXIT_FAILURE);
}
printf("This machine is running %*s\n", osnamelth, osname);
exit(EXIT_SUCCESS);
}
However, the man page linked also notes:
Glibc does not provide a wrapper for this system call; call it using
syscall(2). Or rather... don't call it: use of this system call has
long been discouraged, and it is so unloved that it is likely to
disappear in a future kernel version. Since Linux 2.6.24, uses of this
system call result in warnings in the kernel log. Remove it from your
programs now; use the /proc/sys interface instead.
This system call is available only if the kernel was configured with
the CONFIG_SYSCTL_SYSCALL option.
Please keep in mind that anything you can do with sysctl(), you can also just read() from /proc/sys. Also note that I do understand that the usefulness of that syscall is questionable, I just put it here for reference.
You can also use the sys/utsname.h header file to get the kernel version, hostname, operating system, machine hardware name, etc. More about sys/utsname.h is here. This is an example of getting the current kernel release.
#include <stdio.h> // I/O
#include <sys/utsname.h>
int main(int argc, char const *argv[])
{
struct utsname buff;
printf("Kernel Release = %s\n", buff.release); // kernel release
return 0;
}
This is the same as using the uname command. You can also use the -a option which stands for all information.
uname -r # -r stands for kernel release

Resources