I tried to execute a userspace binary inside the Linux kernel module with call_usermodehelper. I found the launched application is running in root mode. Is it possible to run the application in a user mode, for example, named user1?
int alter_uid_gid(uid_t uid, gid_t gid, struct cred *new)
{
// struct cred *new;
new = prepare_creds();
if (!new)
return -ENOMEM;
new->uid = new->euid = new->suid = new->fsuid = KUIDT_INIT(uid);
new->gid = new->egid = new->sgid = new->fsgid = KGIDT_INIT(gid);
return commit_creds(new);
}
static int init_func(struct subprocess_info *info, struct cred *new)
{
printk("[%d]\n", current->pid);
alter_uid_gid(1000, 1000, new);
return 0;
}
static int user_process_fork(void *data)
{
struct subprocess_info *sub_info;
int ret = 0;
char *path = (char *)data;
char *argv[] = {path, NULL};
static char *envp[] = {"HOME=/", "TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};
sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,
init_func, NULL, NULL);
if (sub_info == NULL) return -ENOMEM;
ret = call_usermodehelper_exec(sub_info, UMH_KILLABLE);
pr_info("%s: ret %d\n", __func__, ret);
do_exit(0);
return ret;
}
Based on Milag's comment, I tried to update the u[g/e/s]idin the init_func() with prepare_creds() and commit_creds(new). In the kernel log, I can see current->uid has changed to 1000. But when I run ps aux, the process is still in root mode. Any idea why?
After I read some kernel sources and posted comments above, OP later showed updates for a developed kmod.
Short answer: yes, it's possible to set different IDs for a user process launched from a kmod.
After passing an init routine to call_usermodehelper_setup(), a related kernel service calls the init routine with a (struct cred *); various uid and gid members can be changed there. For more details, see call_usermodehelper_exec_async()
There are related suggestions for kmod versatility:
add a pair of #define for a set of default uid and gid
add support for module params to set other uid and gid
optionally provide cmd-line params when the module is loaded
For an example, see this link .
Based on #Milag's comment, the following code makes the new userspace process running in user-mode (using ps aux to inspect):
int alter_uid_gid(uid_t uid, gid_t gid, struct cred *new)
{
new->uid = new->euid = new->suid = new->fsuid = KUIDT_INIT(uid);
new->gid = new->egid = new->sgid = new->fsgid = KGIDT_INIT(gid);
return 0;
}
static int init_func(struct subprocess_info *info, struct cred *new)
{
printk("[%d]\n", current->pid);
alter_uid_gid(1000, 1000, new);
return 0;
}
static int user_process_fork(void *data)
{
struct subprocess_info *sub_info;
int ret = 0;
char *path = (char *)data;
char *argv[] = {path, NULL};
static char *envp[] = {"HOME=/", "TERM=linux",
"PATH=/sbin:/bin:/usr/sbin:/usr/bin", NULL};
sub_info = call_usermodehelper_setup(argv[0], argv, envp, GFP_ATOMIC,
init_func, NULL, NULL);
if (sub_info == NULL) return -ENOMEM;
ret = call_usermodehelper_exec(sub_info, UMH_KILLABLE);
pr_info("%s: ret %d\n", __func__, ret);
do_exit(0);
return ret;
}
Related
Why I/O Error causes when I tried ls . command in my fuse filesystem?
My filesystem has a limitation that it only allows mail address type as individual filename and it does not allows sub directory.
Now I want to display a list of file name when use ls . but it does not work.
I understood that it must implement a callback function. (Correspond function is ll_readdir in mycode)
but I have no idea what points are causes the errors.
Update:
Now I use strace command to investigate what system call raise a this error.
According to result of strace, this error caused in getdents64 syscall.
getdents64(3, 0x5611ed000540, 32768) = -1 EIO (Input/output error)
Code1 (implementation of mm:
struct mutex_map {
int counter = 2;
std::mutex _mtx;
std::unordered_map<int, std::string> _data;
std::unordered_map<std::string, int> _rev_data;
public:
int set_value(const char* value) {
std::string s = std::string(value);
std::lock_guard<std::mutex> lock(_mtx);
counter++;
_data[counter] = s;
_rev_data[s] = counter;
return counter;
}
const char* get_value(int key) { return _data[key].c_str(); }
int get_ino(const char* name) { return _rev_data[std::string(name)]; }
};
static mutex_map mm;
Code2: (sendmailfs_stat)
static int sendmailfs_stat(fuse_ino_t ino, struct stat* stbuf,
size_t name_length) {
uid_t uid = getuid();
gid_t gid = getgid();
stbuf->st_ino = ino;
if (ino == 1) {
stbuf->st_mode = S_IFDIR | 0755;
stbuf->st_nlink = 2;
stbuf->st_uid = uid;
stbuf->st_mode = S_IFDIR;
} else {
stbuf->st_mode = S_IFCHR | 0666;
stbuf->st_nlink = 1;
stbuf->st_size = name_length;
stbuf->st_uid = uid;
stbuf->st_gid = gid;
}
return 0;
}
Code 3: (implementation of readdir callback)
static void ll_readdir(fuse_req_t req, fuse_ino_t ino, size_t size, off_t off,
struct fuse_file_info* fi) {
// printf("size_t=%ld, fh=%ld, ino=%ld\n", size, fi->fh, ino);
if (ino == 1) {
off_t o = 0;
size_t rest = size;
size_t res;
char* buf = (char*)calloc(1, size);
struct stat dotst;
sendmailfs_stat(ino, &dotst, strlen("."));
res = fuse_add_direntry(req, buf, rest, ".", &dotst, o);
rest -= res;
o++;
printf("%s\n", "start of loop");
uint64_t num_contain = 0;
for (auto& c : mm._data) {
const char* t = c.second.c_str();
int ino2 = mm.get_ino(t);
struct stat st;
sendmailfs_stat(ino2, &st, strlen(t));
fuse_entry_param e;
e.ino = ino2;
e.attr_timeout = 0;
sendmailfs_stat(ino2, &e.attr, strlen(t));
res = fuse_add_direntry_plus(req, buf, rest, t, &e, o);
o += 1;
rest -= res;
}
fuse_reply_buf(req, buf, size);
}
}
A bit late, but if anyone having this error stumbles upon this thread, they might want to check first whether the filesystem is mounted properly. The Input/output error from getdents64 is symptomatic of a filesystem that was unmounted, but failed for some reason (like a file was still in use when user tried the umount command), so still looks mounted, but no data can be fetched from it.
So in this case, some process could be calling umount (and failing) before you run ls, or the filesystem failed to correctly mount in the first place for some reason.
I have an assignment where I have to transfer the file from a client process to server process using fifo.I have tried to deal with fifo file as the other files we create in the system. It compiled without any error but it didn't execute properly.Can someone please give me an idea about the fifo file structure inside the computer system? What processes and functions are present for it ?Till now, I know how to use create(),read(),write(), open() function for fifo file.Also, I would be grateful if someone could help me to correct my program?
My client and server program are as follows:-
Client Program:-
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd;
char *myfifo ="/tmp/myfifo";
char str[80];
FILE *fp;
char filename[20];
printf("\nEnter filename: ");
gets(filename);
mkfifo(myfifo,0666);
fp = fopen(filename,"r");
if(fp == NULL)
{
printf("\nError opening the file");
exit(1);
}
fd = open(myfifo, O_WRONLY);
while(fgets(str,80,fp)!=NULL)
{
write(fd,str,strlen(str)+1);
}
close(fd);
fclose(fp);
return 0;
}
Client Program:-
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd1;
char *myfifo ="/tmp/myfifo";
char str1[80], filename[20];
FILE *fp1, *fp2;
fd1= open(myfifo, O_RDONLY);
fp1 = fopen(filename,"r");
fp2 = fopen(filename,"w");
while(!feof(fp1))
{
read(fd1,str1,strlen(str1)+1);
fputs(str1,fp2);
}
return 0;
}
Yes, but you have a few small problems in your programs. in the first:
write(fd, str, strlen(str)+1);
is a bit unconventional. This sends the string plus its end-of-string delimiter (\0) into the fd. One doesn't normally do this with strings, strlen(str) is probably what you want.
in the second:
fp1 = fopen(filename,"r");
fp2 = fopen(filename,"w");
filename has not been assigned a value, so both of these opens will almost certainly fail. When they do, they return a NULL pointer, so the first attempt to use them:
while(!feof(fp1))
will likely cause a segment violation. Also, you don't use fp1 anyways, so if feof(fp1) returned 1, it would always return 1. You want to base this loop on when the fifo is exhausted, which means there is no data in it, and nobody has it open for write. So changing this program around a bit yields:
#include<stdio.h>
#include<string.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
int main()
{
int fd1;
char *myfifo ="/tmp/myfifo";
char str1[80];
ssize_t n;
fd1= open(myfifo, O_RDONLY);
while ((n=read(fd1,str1,sizeof str1)) > 0)
{
fwrite(str1, 1, n, stdout);
}
return 0;
}
While this set of changes works, it doesn't address your other question, about using stdio functions with pipes. The answer is yes, and here is another functional rewrite of your second program:
#include<stdio.h>
int main()
{
char *myfifo ="/tmp/myfifo";
FILE *fp;
int c;
if ((fp = fopen(myfifo, "r")) != NULL) {
while ((c = getc(fp)) != EOF) {
putchar(c);
}
fclose(fp);
}
return 0;
}
Also, in the first, the critical bit with stdio:
...
FILE *fi = fopen(myfifo, "a");
while(fgets(str,80,fp)!=NULL)
{
fputs(str, fi);
}
fclose(fi);
...
as in the second, the loop could have been implemented with getc, putc.
A general refinement might be functions like these:
ssize_t FCopy(FILE *in, FILE *out) {
int c;
ssize_t len = 0;
while ((c = getc(in)) != EOF) {
len++;
if (putc(c, out) != c) {
return -len;
}
}
return len;
}
ssize_t FileAppend(char *from, char *to) {
FILE *in, *out;
ssize_t n = 0;
if ((in = fopen(from, "rb")) != NULL) {
if ((out = fopen(to, "ab")) != NULL) {
n = FCopy(in, out);
fclose(out);
} else {
n = -1;
}
fclose(in);
} else {
n = -1;
}
return n;
}
so your main would look more like:
...
char filename[80];
printf("Enter a file to store the data in: ");
if (fgets(filename, sizeof filename, stdin)) {
filename[strlen(filename)-1] = '\0';
if (FileAppend(myfifo, filename) < 0) {
printf("Error: could not save data to %s\n", filename);
}
}
....
I am implementing a system call in Linux. I have a problem: Cannot receive value of parameter from user. My kernel version 4.19.5
This is user code:
int main(){
char *name = (char*)malloc(sizeof(char)*32);
int fd1;
char buf[128];
fd1 = open("/usr/src/linux-4.19.5/abc", O_WRONLY);
if (fd1 == -1) {
printf("ERROR\n");
return EXIT_FAILURE;
}
printf("Enter process to find: ");
scanf (" %[^\n]", name);
write(fd1, name, strlen(name));
long int status = syscall(335, name);
printf("System call returned %ld\n", status);
close(fd1);
return 0;}
But I can't receive value of 'name' in kernel code:
This is kernel code:
asmlinkage long sys_process_name(char* process_name){
struct task_struct *task;
struct tty_struct *my_tty;
my_tty = get_current_tty();
char name[32];
for_each_process(task){
if(strcmp(task->comm,process_name) == 0){
sprintf(name, "PID = %ld\n", (long)task_pid_nr(task));
(my_tty->driver->ops->write) (my_tty, name, strlen(name)+1);
}
}
return 0;}
Value of process_name is null.
I am a windows driver programmer, who is also a complete beginner in linux kernel development. im trying out simple linux kernel modules.
I have written a parallel port driver as in the following code and could successfully load this driver.
I have created a char device node using command mknod with same major and minor numbers as in the code. I have tried reading from the file using cat command and i found it was working fine. While i tried writing the same device using command cat in.txt>/dev/parallel_dev the prompt shows a message "killed" and in dmesg, it has shown unable to handle kernel NULL pointer exception.
#include<linux/init.h>
#include<linux/module.h>
#include<linux/kernel.h> //kprintf
#include<linux/module.h> //kmalloc
#include<linux/types.h>
#include<linux/fs.h> //fileoperations,register_chrdev
#include<linux/slab.h>
#include<linux/uaccess.h> //copy from/to user
#include<linux/ioport.h> //check_region
#include<linux/interrupt.h>
#include<linux/delay.h>
static int parallel_open(struct inode* p_node, struct file* p_file);
static int parallel_release(struct inode* p_node, struct file* p_file);
static ssize_t parallel_read(struct file* p_file, char* buf, size_t count, loff_t* f_pos);
static ssize_t parallel_write(struct file* p_file, const char* buf, size_t count, loff_t* f_pos);
static irqreturn_t parallel_interrupt(int irq,void* dev);
static const int major_no = 709;
static const char* dev_name = "parallel_tryout";
#define par_base 0x378
int port;
int* parallel_buffer;
struct file_operations fops = {
.read = parallel_read,
.write = parallel_write,
.open = parallel_open,
.release = parallel_release,
};
static int parallel_init(void)
{
int reg_result;
int irq_req_result;
unsigned int irq_num = 7;
printk("Entered init");
printk("Initializing parallelport dev with major %d",major_no);
reg_result = register_chrdev(major_no, dev_name, 0);
if(reg_result < 0 )
{
printk("Error registering char dev: %s",dev_name);
}
parallel_buffer = kmalloc(16,GFP_KERNEL);
if(parallel_buffer==NULL)
{
printk("Error allocating memory for: %s",dev_name);
}
printk("Port registration");
request_region(0x378,1,"parallelport");
if(port)
{
printk("Error allocating port");
}
parallel_buffer = inb(par_base);
if(parallel_buffer != NULL)
{
printk("Allocated buffer");
}
irq_req_result = request_irq(irq_num,parallel_interrupt,IRQF_SHARED,dev_name,parallel_buffer);
return 0;
}
static void parallel_exit(void)
{
unregister_chrdev(major_no, dev_name);
release_region(0x378,1);
}
static int parallel_open(struct inode* p_node, struct file* p_file)
{
printk("parallel driver opened from device: %s",dev_name);
return 0;
}
static int parallel_release(struct inode* p_node, struct file* p_file)
{
printk("Memory driver closed from device: %s",dev_name);
return 0;
}
static ssize_t parallel_read(struct file* p_file, char* buf, size_t count, loff_t* f_pos)
{
copy_to_user(buf,parallel_buffer,1);
printk("Copied data:%s ",buf);
printk("Memory driver read from device: %s",dev_name);
return 0;
}
static ssize_t parallel_write(struct file* p_file, const char* buf, size_t count, loff_t* f_pos)
{
copy_from_user(parallel_buffer,buf,1);
printk("Copied data:%s ",parallel_buffer);
printk("Memory driver written from device: %s",dev_name);
return 0;
}
static irqreturn_t parallel_interrupt(int irq,void* dev)
{
irqreturn_t return_value;
printk("parallel interrupt detected!!!!");
return return_value;
}
module_init(parallel_init);
module_exit(parallel_exit);
what could be the possible reason for this, and thanks in advance.
How do I create my own bio request to read a sector from the disk drive ?
I am trying the following but it freezes the system.
static void read_bio()
{
struct bio *b;
struct page *p;
b = bio_alloc(GFP_KERNEL, 1);
if (!b) {
printk(KERN_INFO "bio allocation failed\n");
}
bio_init(b);
b->bi_sector = 10000;
b->bi_bdev = bd; /* "/dev/sda1" */
b->bi_end_io = bio_end_clone;
p = alloc_page(GFP_KERNEL);
if (!p) {
printk(KERN_INFO "page allocation failed\n");
}
bio_add_page(b, p, PAGE_SIZE, 0);
b->bi_private = p;
bio_get(b);
submit_bio(READ, b);
printk(KERN_DEBUG "submit read request\n");
}
It is an old question, but anyway here is the code for reading, I hope it will help someone:
int readPage(struct block_device *device, sector_t sector, int size,
struct page *page)
{
int ret;
struct completion event;
struct bio *bio = bio_alloc(device, 1, REQ_OP_READ, GFP_NOIO);
bio->bi_iter.bi_sector = sector;
bio_add_page(bio, page, size, 0);
init_completion(&event);
bio->bi_private = &event;
bio->bi_end_io = readComplete;
submit_bio(bio);
wait_for_completion(&event);
ret = test_bit(BIO_UPTODATE, &bio->bi_flags);
bio_put(bio);
return ret;
}
And for writing:
void writePage(struct block_device *device,
sector_t sector, int size, struct page *page)
{
struct bio *bio = bio_alloc(device, 1, REQ_OP_WRITE, GFP_NOIO);
bio->bi_iter.bi_sector = sector;
bio_add_page(bio, page, size, 0);
bio->bi_end_io = writeComplete;
submit_bio(bio);
}
page can be allocated with alloc_page(GFP_KERNEL). Also for changing data in page use page_address(page). It returns void* so you can interpret that pointer as whatever you want.