Error when adding my custom syscall to kernel - c

So i've been adding my custom syscall to kernel. This is my syscall's source code:
//sys_procmem.c. This syscall helps users to show the memory layout of a specific process.
#include <linux/linkage.h>
#include <linux/sched.h>
struct proc_segs {
unsigned long my_id;
unsigned long start_code;
unsigned long end_code;
unsigned long start_data;
unsigned long end_data;
unsigned long start_heap;
unsigned long end_heap;
unsigned long start_stack;
};
asmlinkage long sys_procmem( int pid, struct proc_segs * info) {
struct task_struct *task;
info->my_id = 1512853;
for_each_process(task){
if((int) task->pid==pid){
info->start_code=task->mm->start_code;
info->end_code=task->mm->end_code;
info->start_data=task->mm->start_data;
info->end_data=task->mm->end_data;
info->start_heap=task->mm->start_brk;
info->end_heap=task->mm->brk;
info->start_stack=task->mm->start_stack;
return 0;
}
}
return -1;
}
Everything seems fine until when i write a testing program after i compile my kernel, source code:
//sys.c
#include <sys/syscall.h>
#include <stdio.h>
#include <unistd.h>
#define SIZE 100
int main() {
long sysvalue;
unsigned long info[SIZE];
sysvalue = syscall([number_32], 1, info)
printf("My ID: %lu\n", info[0]);
}
I compiled the program with:
gcc sys.c -o sys
Then i ran it with:
./sys
I got this error:
Please help me, thank you guys for reading :)

Related

Error when copying data from kernel to user space using copy_to_user()

#include <linux/kernel.h>
#include <linux/uaccess.h>
unsigned long long cnt = 0;
asmlinkage long sys_customcall(unsigned long long __user *output)
{
unsigned long err;
err = copy_to_user(output, &cnt, sizeof(unsigned long long));
return err;
}
I'm implementing a simple system call. I would like to copy a value from the kernel (cnt) to user pointer (output). However, when I run the code err = 8 which is sizeof(unsigned long long) - user side values are not changing. What have I done wrong?
I'm using linux-kernel version 5.4.59.
The user-level code is as following.
#include <stdio.h>
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
int main()
{
unsigned long long cnt=0;
long int err = syscall(436, &cnt);
printf("System call returned %lu\n", err);
printf("System call returned %llu\n", cnt);
return 0;
}
Problem got solved when I changed asmlinkage to SYSCALL_DEFINEx macro.
It's not certain but I believe it had something to do with x86_64 system call wrapper.
This link was helpful.
Thank you all.

What is missing in my syscall implementation?

I tried many guides and for some reason, my syscall isnt implementing correctly. ERRNO is 38 (function not implemented). Here are my steps to creating a basic one for me as a beginner:
I am doing this on a raspberry pi 64 bit. with linux 4.14.93
inside "/linux/" folder:
I created a folder called "mycall" with files mycall.c, mycall.h, & Makefile
mycall.c:
#include <linux/kernel.h>
#include <linux/init.h>
#include <sched.h>
#include <syscalls.h>
#include <linux/unistd.h>
#include "mycall.h"
asmlinkage long sys_mysyscall(int *id, int username, int *size)
{
printk("hello");
return 0;
}
mycall.h:
asmlinkage long sys_mysyscall(int *id, int username, int *size);
Makefile:
obj-y := mycall.o
then I went into all of the places where I THOUGHT i should declare the syscall.
// inside of /linux/include/linux/syscalls.h
asmlinkage long sys_mysyscall(int __user *myid, int username, int __user *size);
then to
// inside of /linux/arch/arm/tools/syscall.tbl
398 common mysyscall sys_mysyscall
finally i added
// inside of /linux/Makefile
core-y += kernel/ certs/ mm/ fs/ ipc/ security/ crypt/ block/ mycall/
thats my set up. then the final thing I do is make a userspace in /linux directory
userspace.c:
#include <linux/kernel.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <pwd.h>
int main ()
{
int id = 0;
int username = 7;
int size = 2;
int ret_val = syscall(398, &id, username, &size);
printf("%d\n", ret_val);
return 0;
}
// recompiling & copying . then I run the userspace again
cd ~/linux
KERNEL=kernel7
make bcm2709_defconfig
make -j4 zImage modules dtbs
sudo make INSTALL_MOD_PATH=/root modules_install
sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
sudo cp arch/arm/boot/zImage /boot/$KERNEL.img
the return value is -1 so this doesn't work.
please let me know what I'm doing wrong. I have read several implementation guides and have no idea how I'm implementing this wrong.

Is SCHED_DEADLINE officially supported in Ubuntu 16.04?

Currently I'm running Ubuntu 16.04 with linux kernel version to be 4.16. I wrote a dummy program that changes its scheduler to SCHED_DEADLINE. But when I tried to compile it, it cannot find definition of structs and macros needed for SCHED_DEADLINE. Most of the code snippet was taken from here (page 24). Below is the test program:
#define _GNU_SOURCE
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sched.h>
int main(int argc, char* argv[]) {
struct sched_attr attr;
attr.size = sizeof(attr);
attr.sched_policy = SCHED_DEADLINE;
attr.sched_runtime = 30000000;
attr.sched_period = 100000000;
attr.sched_deadline = attr.sched_period;
if (sched_setattr(gettid(), &attr, 0))
perror("sched_setattr()");
return 0;
}
Here's the output of the compilation:
sched_deadline.c: In function ‘main’:
sched_deadline.c:11:20: error: storage size of ‘attr’ isn’t known
struct sched_attr attr;
^
sched_deadline.c:12:21: error: invalid application of ‘sizeof’ to incomplete type ‘struct attr’
attr.size = sizeof(struct attr);
^
sched_deadline.c:13:22: error: ‘SCHED_DEADLINE’ undeclared (first use in this function)
attr.sched_policy = SCHED_DEADLINE;
My gcc version:
gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9)
However, the sample code posted in the official website works for me, but the sample code manually defines all the needed macros and system calls in the program. My goal was to compile the application without adding those definitions, which should already be included in the newest kernel version. I have seen various places saying that SCHED_DEADLINE is officially supported after Linux 3.14.10, and upgrading the kernel would automatically solve this issue.
Things I've tried:
Recompiling 4.16 kernel. Previously I thought I need to turn on a switch in config file, but I was not able to find it.
Look into /usr/include/linux/sched.h. Clearly the macros are defined in this header file, but somehow my compiler cannot find it.
I also looked into other posts in the community, but all those questions are for older linux (pre 3.14.10).
You need to include #include <linux/sched.h>
But for the definition of sched_setattr() and gettid(), see the link posted by #CraigEstey
The reason about that, it that glibc will not add function wrappers of linux specific syscall.
For example for gettid(), in the manual we can read this:
Note: There is no glibc wrapper for this system call; see NOTES.
Glibc does not provide a wrapper for this system call; call it using
syscall(2).
The thread ID returned by this call is not the same thing as a POSIX thread ID
Have a look at this article: https://lwn.net/Articles/711058/
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sched.h>
#include <linux/sched.h>
#include <sys/types.h>
struct sched_attr {
uint32_t size;
uint32_t sched_policy;
uint64_t sched_flags;
/* SCHED_NORMAL, SCHED_BATCH */
int32_t sched_nice;
/* SCHED_FIFO, SCHED_RR */
uint32_t sched_priority;
/* SCHED_DEADLINE (nsec) */
uint64_t sched_runtime;
uint64_t sched_deadline;
uint64_t sched_period;
};
int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
return syscall(__NR_sched_setattr, pid, attr, flags);
}
int main(int argc, char* argv[]) {
struct sched_attr attr = {
.size = sizeof(attr),
.sched_policy = SCHED_DEADLINE,
.sched_runtime = 30000000,
.sched_period = 100000000,
.sched_deadline = 100000000
};
pid_t tid = syscall(SYS_gettid);
if (sched_setattr(tid, &attr, 0))
perror("sched_setattr()");
return 0;
}
Or a more shorter code, without the redefinition of struct sched_attr
#define _GNU_SOURCE
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/sched/types.h>
#include <linux/sched.h>
#include <sys/types.h>
int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
return syscall(__NR_sched_setattr, pid, attr, flags);
}
int main(int argc, char* argv[]) {
struct sched_attr attr = {
.size = sizeof(attr),
.sched_policy = SCHED_DEADLINE,
.sched_runtime = 30000000,
.sched_period = 100000000,
.sched_deadline = 100000000
};
pid_t tid = syscall(SYS_gettid);
if (sched_setattr(tid, &attr, 0))
perror("sched_setattr()");
return 0;
}
But this needs to be executed as root, otherwise I got sched_setattr(): Operation not permitted
Or the application needs to have the right linux capabilities.

C - Linux Kernel - Assistance with current_uid()

I have been working on part of an assignment which I am having trouble fixing. The requirement was to intercept the system call open and to replace it with a new system open call only for regular users and would print out the user id and filename in the system log. Otherwise it would just execute the standard system open call. Here is part of what I have that is troubling me:
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/syscalls.h>
unsigned long **sys_call_table;
asmlinkage long (*ref_sys_open)(const char *filename, int flags, umode_t mode);
asmlinkage long (*ref_sys_close)(unsigned int fd);
asmlinkage long new_sys_open(const char *filename, int flags, umode_t mode) {
if (current_uid() >= 1000) {
printk(KERN_INFO "User %d is opening file: %s\n", current_uid(), filename);
} else {
(*ref_sys_open)(filename, flags, mode);
}
return 0;
}
The problem I am getting is that the returning value of current_uid() is a struct with type kuid_t. I looked into it further and found that the struct looks like this:
typedef struct {
uid_t val;
} kuid_t;
I was wondering how do I compare int 1000 to type uid_t val?
On a side note, did I call the old versions of the system call correctly?
from here, uid_t is just a typedef of __kernel_uid32_t, which is unsigned int according to here

linux kernel module assignment makes integer from pointer without a cast

this is simple sys_call_table hooking code
#include <asm/unistd.h>
#include <linux/autoconf.h>
#include <linux/in.h>
#include <linux/init_task.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/kmod.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/skbuff.h>
#include <linux/stddef.h>
#include <linux/string.h>
#include <linux/syscalls.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/unistd.h>
#include <linux/version.h>
#include <linux/workqueue.h>
ssize_t *sys_call_table = (ssize_t *)0xc0026e04;
asmlinkage ssize_t (*orig_open)(const char *pathname, int flags);
asmlinkage ssize_t hacked_open(const char *pathname, int flags)
{
printk(KERN_INFO "SYS_OPEN called : %s\n", pathname);
return orig_open(pathname, flags);
}
int init_module(void)
{
orig_open = sys_call_table[__NR_open]; /* line 33 */
sys_call_table[__NR_open] = hacked_open; /* line 34 */
return 0;
}
void cleanup_module(void)
{
sys_call_table[__NR_open] = orig_open; /* line 40 */
}
MODULE_LICENSE("GPL");
i got an warning like below
this code works fine but i want to delete warnings. how can i do?
/home/tester/tools/lkm/a.c: In function 'init_module':
/home/tester/tools/lkm/a.c:33: warning: assignment makes integer from pointer without a cast
/home/tester/tools/lkm/a.c:34: warning: assignment makes integer from pointer without a cast
/home/tester/tools/lkm/a.c: In function 'cleanup_module':
/home/tester/tools/lkm/a.c:40: warning: assignment makes integer from pointer without a cast
If you want to silence your compiler, you have to add typecasts (even if it is often a bad idea, this is how your compiler turns it).
ssize_t *sys_call_table = (ssize_t *)0xc0026e04;
typedef ssize_t (*ftype)(const char *, int);
ftype orig_open;
ssize_t hacked_open(const char *pathname, int flags)
{
printf("SYS_OPEN called : %s\n", pathname);
return orig_open(pathname, flags);
}
int init_module(void)
{
orig_open = (ftype)sys_call_table[__NR_open];
sys_call_table[__NR_open] = (ssize_t)hacked_open;
return 0;
}
When you look at line 33 you will see the problem:
orig_open = sys_call_table[__NR_open];
You have defined sys_call_table to be a pointer to integer. That's the reason for the warning.
Same problem with the other lines. If you define sys_call_table properly, the warnings will go away.
You should at least define it as an array of pointers or pointer to pointers, because if ssize_t is only 32 bit on 64 bit system, you might truncate the 64 bit addresses to a 32 bit integer.

Resources