From 456c9250b94cd8044c3d5e4344f1fa8000a61f96 Mon Sep 17 00:00:00 2001 From: hinus Date: Thu, 29 Jul 2021 10:27:09 +0800 Subject: [PATCH] Title: System call 'execve' Issue: https://gitee.com/hinus/linux_kernel_011/issues/I41VU6 Description: load root from root011.img, it seems that root012 is bad. --- fs/Makefile | 6 +- fs/buffer.c | 5 + fs/exec.c | 226 ++++++++++++++++++++++++++++++++++++++++++ fs/inode.c | 2 +- include/a.out.h | 152 ++++++++++++++++++++++++++++ include/asm/segment.h | 22 ++++ include/linux/mm.h | 2 + include/linux/sched.h | 5 +- include/linux/sys.h | 4 +- include/sys/stat.h | 4 + include/unistd.h | 1 + kernel/main.c | 17 +++- kernel/sys_call.S | 10 +- lib/Makefile | 5 +- lib/execve.c | 5 + mm/memory.c | 26 ++++- 16 files changed, 479 insertions(+), 13 deletions(-) create mode 100644 fs/exec.c create mode 100644 include/a.out.h create mode 100644 lib/execve.c diff --git a/fs/Makefile b/fs/Makefile index 7dfe35f..e2f2bcb 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -3,7 +3,8 @@ LD := ld CCFLAG := -I../include -nostdinc -ffreestanding -Wall -fomit-frame-pointer -fno-stack-protector -fno-pic -c -m32 LDFLAG := -Ttext 0x0 -s --oformat binary -m elf_i386 INCDIR := ../include -OBJS := read_write.o buffer.o super.o open.o file_table.o inode.o namei.o fcntl.o char_dev.o bitmap.o truncate.o +OBJS := read_write.o buffer.o super.o open.o file_table.o inode.o namei.o fcntl.o char_dev.o \ + bitmap.o truncate.o exec.o fs.o : $(OBJS) $(LD) -m elf_i386 -r -o $@ $^ @@ -41,6 +42,9 @@ bitmap.o : bitmap.c truncate.o : truncate.c $(GCC) $(CCFLAG) -o $@ $< +exec.o : exec.c + $(GCC) $(CCFLAG) -o $@ $< + clean : -rm *.o diff --git a/fs/buffer.c b/fs/buffer.c index 3f12e16..f966d40 100644 --- a/fs/buffer.c +++ b/fs/buffer.c @@ -249,6 +249,11 @@ void bread_page(unsigned long address,int dev,int b[4]) { struct buffer_head * bh[4]; int i; + for (i = 0; i < 4; i++) { + printk("%d,", b[i]); + } + printk("\n"); + for (i = 0; i < 4; i++) { if (b[i]) { if ((bh[i] = getblk(dev,b[i]))) { diff --git a/fs/exec.c b/fs/exec.c new file mode 100644 index 0000000..1ed1682 --- /dev/null +++ b/fs/exec.c @@ -0,0 +1,226 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define MAX_ARG_PAGES 32 + +static unsigned long * create_tables(char * p,int argc,int envc) { + unsigned long *argv,*envp; + unsigned long * sp; + + sp = (unsigned long *) (0xfffffffc & (unsigned long) p); + sp -= envc+1; + + envp = sp; + sp -= argc+1; + argv = sp; + put_fs_long((unsigned long)envp,--sp); + put_fs_long((unsigned long)argv,--sp); + put_fs_long((unsigned long)argc,--sp); + while (argc-->0) { + put_fs_long((unsigned long) p,argv++); + while (get_fs_byte(p++)) /* nothing */ ; + } + + put_fs_long(0,argv); + while (envc-->0) { + put_fs_long((unsigned long) p,envp++); + while (get_fs_byte(p++)) /* nothing */ ; + } + + put_fs_long(0,envp); + return sp; +} + +static int count(char ** argv) { + int i=0; + char ** tmp; + if ((tmp = argv)) { + while (get_fs_long((unsigned long *) (tmp++))) + i++; + } + return i; +} + +static unsigned long copy_strings(int argc,char ** argv,unsigned long *page, + unsigned long p, int from_kmem) { + char *tmp, *pag; + int len, offset = 0; + unsigned long old_fs, new_fs; + + if (!p) + return 0; + new_fs = get_ds(); + old_fs = get_fs(); + + if (from_kmem==2) + set_fs(new_fs); + + while (argc-- > 0) { + if (from_kmem == 1) + set_fs(new_fs); + if (!(tmp = (char *)get_fs_long(((unsigned long *)argv)+argc))) + panic("argc is wrong"); + if (from_kmem == 1) + set_fs(old_fs); + len = 0; + do { + len++; + } while (get_fs_byte(tmp++)); + + if (p-len < 0) { + set_fs(old_fs); + return 0; + } + } + + while (len) { + --p; --tmp; --len; + if (--offset < 0) { + offset = p % PAGE_SIZE; + if (from_kmem==2) + set_fs(old_fs); + if (!(pag = (char *) page[p/PAGE_SIZE])) { + page[p / PAGE_SIZE] = (unsigned long*) get_free_page(); + pag = (char*) page[p / PAGE_SIZE]; + + if (!pag) + return 0; + } + if (from_kmem==2) + set_fs(new_fs); + } + *(pag + offset) = get_fs_byte(tmp); + } + + if (from_kmem==2) + set_fs(old_fs); + + return p; +} + +static unsigned long change_ldt(unsigned long text_size,unsigned long * page) { + unsigned long code_limit,data_limit,code_base,data_base; + int i; + + code_limit = TASK_SIZE; + data_limit = TASK_SIZE; + code_base = get_base(current->ldt[1]); + data_base = code_base; + set_base(current->ldt[1],code_base); + set_limit(current->ldt[1],code_limit); + set_base(current->ldt[2],data_base); + set_limit(current->ldt[2],data_limit); + __asm__("pushl $0x17\n\tpop %%fs"::); + data_base += data_limit - LIBRARY_SIZE; + for (i=MAX_ARG_PAGES-1 ; i>=0 ; i--) { + data_base -= PAGE_SIZE; + if (page[i]) + put_dirty_page(page[i],data_base); + } + return data_limit; +} + +int do_execve(unsigned long * eip,long tmp,char * filename, + char ** argv, char ** envp) { + struct m_inode * inode; + struct buffer_head * bh; + struct exec ex; + unsigned long page[MAX_ARG_PAGES]; + int i,argc,envc; + int e_uid, e_gid; + int retval; + int sh_bang = 0; + unsigned long p=PAGE_SIZE*MAX_ARG_PAGES-4; + + if ((0xffff & eip[1]) != 0x000f) + panic("execve called from supervisor mode"); + for (i=0 ; ii_mode)) { + retval = -EACCES; + goto exec_error2; + } + + i = inode->i_mode; + e_uid = (i & S_ISUID) ? inode->i_uid : current->euid; + e_gid = (i & S_ISGID) ? inode->i_gid : current->egid; + if (current->euid == inode->i_uid) + i >>= 6; + else if (in_group_p(inode->i_gid)) + i >>= 3; + if (!(i & 1) && + !((inode->i_mode & 0111) && suser())) { + retval = -ENOEXEC; + goto exec_error2; + } + + if (!(bh = bread(inode->i_dev,inode->i_zone[0]))) { + retval = -EACCES; + goto exec_error2; + } + ex = *((struct exec *) bh->b_data); + brelse(bh); + if (N_MAGIC(ex) != ZMAGIC || ex.a_trsize || ex.a_drsize || + ex.a_text+ex.a_data+ex.a_bss>0x3000000 || + inode->i_size < ex.a_text+ex.a_data+ex.a_syms+N_TXTOFF(ex)) { + retval = -ENOEXEC; + goto exec_error2; + } + + if (N_TXTOFF(ex) != BLOCK_SIZE) { + printk("%s: N_TXTOFF != BLOCK_SIZE. See a.out.h.", filename); + retval = -ENOEXEC; + goto exec_error2; + } + + if (!sh_bang) { + p = copy_strings(envc,envp,page,p,0); + p = copy_strings(argc,argv,page,p,0); + if (!p) { + retval = -ENOMEM; + goto exec_error2; + } + } + + if (current->executable) + iput(current->executable); + current->executable = inode; + + free_page_tables(get_base(current->ldt[1]),get_limit(0x0f)); + free_page_tables(get_base(current->ldt[2]),get_limit(0x17)); + + p += change_ldt(ex.a_text,page); + p -= LIBRARY_SIZE + MAX_ARG_PAGES*PAGE_SIZE; + p = (unsigned long) create_tables((char *)p,argc,envc); + + current->brk = ex.a_bss + + (current->end_data = ex.a_data + + (current->end_code = ex.a_text)); + current->start_stack = p & 0xfffff000; + current->suid = current->euid = e_uid; + current->sgid = current->egid = e_gid; + printk("0x%x, 0x%x, eip is 0x%x, 0x%x\n", ex.a_entry, p, eip[0], eip[3]); + eip[0] = ex.a_entry; + eip[3] = p; + return 0; +exec_error2: + iput(inode); +exec_error1: + for (i=0 ; ii_zone[block] = new_block(inode->i_dev))) { inode->i_dirt = 1; } - return inode->i_zone[block]; } + return inode->i_zone[block]; } block -= 7; if (block < 512) { diff --git a/include/a.out.h b/include/a.out.h new file mode 100644 index 0000000..8cd8fb3 --- /dev/null +++ b/include/a.out.h @@ -0,0 +1,152 @@ +#ifndef _A_OUT_H +#define _A_OUT_H + +#define __GNU_EXEC_MACROS__ + +struct exec { + unsigned long a_magic; + unsigned a_text; + unsigned a_data; + unsigned a_bss; + unsigned a_syms; + unsigned a_entry; + unsigned a_trsize; + unsigned a_drsize; +}; + +#ifndef N_MAGIC +#define N_MAGIC(exec) ((exec).a_magic) +#endif + +#ifndef OMAGIC +#define OMAGIC 0407 +#define NMAGIC 0410 +#define ZMAGIC 0413 +#endif + +#ifndef N_BADMAG +#define N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) +#endif + +#define _N_BADMAG(x) \ + (N_MAGIC(x) != OMAGIC && N_MAGIC(x) != NMAGIC \ + && N_MAGIC(x) != ZMAGIC) + +#define _N_HDROFF(x) (SEGMENT_SIZE - sizeof (struct exec)) + +#ifndef N_TXTOFF +#define N_TXTOFF(x) \ + (N_MAGIC(x) == ZMAGIC ? SEGMENT_SIZE : sizeof (struct exec)) +#endif + +#ifndef N_DATOFF +#define N_DATOFF(x) (N_TXTOFF(x) + (x).a_text) +#endif + +#ifndef N_TRELOFF +#define N_TRELOFF(x) (N_DATOFF(x) + (x).a_data) +#endif + +#ifndef N_DRELOFF +#define N_DRELOFF(x) (N_TRELOFF(x) + (x).a_trsize) +#endif + +#ifndef N_SYMOFF +#define N_SYMOFF(x) (N_DRELOFF(x) + (x).a_drsize) +#endif + +#ifndef N_STROFF +#define N_STROFF(x) (N_SYMOFF(x) + (x).a_syms) +#endif + +#ifndef N_TXTADDR +#define N_TXTADDR(x) 0 +#endif + +#define PAGE_SIZE 4096 +#define SEGMENT_SIZE 1024 + +#define _N_SEGMENT_ROUND(x) (((x) + SEGMENT_SIZE - 1) & ~(SEGMENT_SIZE - 1)) +#define _N_TXTENDADDR(x) (N_TXTADDR(x)+(x).a_text) + +#ifndef N_DATADDR +#define N_DATADDR(x) \ + (N_MAGIC(x)==OMAGIC? (_N_TXTENDADDR(x)) \ + : (_N_SEGMENT_ROUND (_N_TXTENDADDR(x)))) +#endif + +#ifndef N_BSSADDR +#define N_BSSADDR(x) (N_DATADDR(x) + (x).a_data) +#endif + +#ifndef N_NLIST_DECLARED +struct nlist { + union { + char *n_name; + struct nlist *n_next; + long n_strx; + } n_un; + + unsigned char n_type; + char n_other; + short n_desc; + unsigned long n_value; +}; +#endif + +#ifndef N_UNDF +#define N_UNDF 0 +#endif +#ifndef N_ABS +#define N_ABS 2 +#endif +#ifndef N_TEXT +#define N_TEXT 4 +#endif +#ifndef N_DATA +#define N_DATA 6 +#endif +#ifndef N_BSS +#define N_BSS 8 +#endif +#ifndef N_COMM +#define N_COMM 10 +#endif +#ifndef N_FN +#define N_FN 15 +#endif + +#ifndef N_EXT +#define N_EXT 1 +#endif +#ifndef N_TYPE +#define N_TYPE 036 +#endif +#ifndef N_STAB +#define N_STAB 0340 +#endif + +#define N_INDR 0xa + +/* constants for ld. */ +#define N_SETA 0x14 +#define N_SETT 0x16 +#define N_SETD 0x18 +#define N_SETB 0x1A + +#define N_SETV 0x1C + +#ifndef N_RELOCATION_INFO_DECLARED +struct relocation_info { + int r_address; + unsigned int r_symbolnum:24; + unsigned int r_pcrel:1; + unsigned int r_length:2; + unsigned int r_extern:1; + unsigned int r_pad:4; +}; +#endif + +#endif diff --git a/include/asm/segment.h b/include/asm/segment.h index a094cb2..8d51e34 100644 --- a/include/asm/segment.h +++ b/include/asm/segment.h @@ -11,9 +11,31 @@ static inline void put_fs_byte(char val,char *addr) { __asm__ ("movb %0,%%fs:%1"::"r" (val),"m" (*addr)); } +extern inline unsigned long get_fs_long(const unsigned long *addr) { + unsigned long _v; + __asm__ ("movl %%fs:%1,%0":"=r" (_v):"m" (*addr)); + return _v; +} + extern inline void put_fs_long(unsigned long val,unsigned long * addr) { __asm__ ("movl %0,%%fs:%1"::"r" (val),"m" (*addr)); } +extern inline unsigned long get_fs() { + unsigned short _v; + __asm__("mov %%fs,%%ax":"=a" (_v):); + return _v; +} + +extern inline unsigned long get_ds() { + unsigned short _v; + __asm__("mov %%ds,%%ax":"=a" (_v):); + return _v; +} + +extern inline void set_fs(unsigned long val) { + __asm__("mov %0,%%fs"::"a" ((unsigned short) val)); +} + #endif diff --git a/include/linux/mm.h b/include/linux/mm.h index 5e383c5..e7d2e6d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -20,6 +20,8 @@ extern unsigned long HIGH_MEMORY; #define MAP_NR(addr) (((addr)-LOW_MEM)>>12) #define USED 100 +#define PAGE_DIRTY 0x40 + extern unsigned char mem_map [ PAGING_PAGES ]; #endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 4196cda..1791e7d 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -5,6 +5,7 @@ #define NR_TASKS 64 #define TASK_SIZE 0x04000000 +#define LIBRARY_SIZE 0x00400000 #if (TASK_SIZE & 0x3fffff) #error "TASK_SIZE must be multiple of 4M" @@ -104,7 +105,7 @@ struct task_struct { 15, \ 15, \ 0, \ -/* ec,brk... */ 0,0,0,0,0,0, \ +/* ec,brk... */ 0,0,0,0,0, \ &init_task.task, 0, 0, 0,\ 0,0,0,0,0,0, \ 0,0022,NULL,NULL,NULL,NULL,0, \ @@ -173,7 +174,7 @@ __asm__("movw %%dx,%0\n\t" \ ::"m" (*(addr)), \ "m" (*((addr)+6)), \ "d" (limit) \ - :"dx") + :) #define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base ) #define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 ) diff --git a/include/linux/sys.h b/include/linux/sys.h index 36f5fd9..13846cf 100644 --- a/include/linux/sys.h +++ b/include/linux/sys.h @@ -9,7 +9,7 @@ extern int sys_open(); //extern int sys_creat(); //extern int sys_link(); //extern int sys_unlink(); -//extern int sys_execve(); +extern int sys_execve(); extern int sys_chdir(); //extern int sys_time(); //extern int sys_mknod(); @@ -99,7 +99,7 @@ fn_ptr sys_call_table[] = { 0, //sys_link, 0, //sys_unlink, - 0, //sys_execve, + sys_execve, sys_chdir, 0, //sys_time, 0, //sys_mknod, diff --git a/include/sys/stat.h b/include/sys/stat.h index 87a0247..2c6a15e 100644 --- a/include/sys/stat.h +++ b/include/sys/stat.h @@ -10,6 +10,10 @@ #define S_IFCHR 0020000 #define S_IFBLK 0060000 +#define S_ISUID 0004000 +#define S_ISGID 0002000 +#define S_ISVTX 0001000 + #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) diff --git a/include/unistd.h b/include/unistd.h index ed3ca88..a202200 100644 --- a/include/unistd.h +++ b/include/unistd.h @@ -159,6 +159,7 @@ int sync(); int open(const char * filename, int flag, ...); int write(int fildes, const char * buf, off_t count); int read(int fildes, const char * buf, off_t count); +int execve(const char * filename, char ** argv, char ** envp); int rmdir(const char* pathname); int chdir(const char* pathname); diff --git a/kernel/main.c b/kernel/main.c index 908cda0..2bfc461 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -76,8 +76,13 @@ static void time_init() { startup_time = kernel_mktime(&time); } -void main(void) -{ +static char * argv_rc[] = { "/bin/sh", NULL }; +static char * envp_rc[] = { "HOME=/", NULL ,NULL }; + +static char * argv[] = { "-/bin/sh",NULL }; +static char * envp[] = { "HOME=/usr/root", NULL, NULL }; + +void main() { ROOT_DEV = ORIG_ROOT_DEV; drive_info = DRIVE_INFO; @@ -134,13 +139,19 @@ int printf(const char* fmt, ...) { } void init() { - int i; + int pid, i; char a[10]; setup((void *) &drive_info); (void)open("/dev/tty0", O_RDWR, 0); (void) dup(0); (void) dup(0); + /* + if (!(pid=fork())) { + printf("read to start shell\n"); + execve("/bin/sh",argv,envp); + }*/ + while (1) { i = read(0, a, 9); a[i - 1] = 0; diff --git a/kernel/sys_call.S b/kernel/sys_call.S index 8cc3adb..cb4298d 100644 --- a/kernel/sys_call.S +++ b/kernel/sys_call.S @@ -1,7 +1,7 @@ .code32 .text .globl system_call, timer_interrupt, sys_fork -.globl hd_interrupt, floppy_interrupt +.globl hd_interrupt, floppy_interrupt, sys_execve EAX = 0x00 EBX = 0x04 @@ -74,6 +74,14 @@ timer_interrupt: addl $4, %esp jmp ret_from_sys_call +.align 4 +sys_execve: + lea EIP(%esp), %eax + pushl %eax + call do_execve + addl $4, %esp + ret + .align 4 sys_fork: call find_empty_process diff --git a/lib/Makefile b/lib/Makefile index 8505f39..331f5a5 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -2,7 +2,7 @@ AR := ar LD := ld GCC := gcc CCFLAG := -m32 -I../include -nostdinc -ffreestanding -fno-pic -Wall -fomit-frame-pointer -fno-stack-protector -c -OBJS := ctype.o write.o string.o open.o dup.o +OBJS := ctype.o write.o string.o open.o dup.o execve.o lib.a : $(OBJS) $(AR) rcs $@ $^ @@ -23,6 +23,9 @@ open.o : open.c dup.o : dup.c $(GCC) $(CCFLAG) -o $@ $< +execve.o : execve.c + $(GCC) $(CCFLAG) -o $@ $< + clean : -rm *.o -rm lib.a diff --git a/lib/execve.c b/lib/execve.c new file mode 100644 index 0000000..bf937e3 --- /dev/null +++ b/lib/execve.c @@ -0,0 +1,5 @@ +#define __LIBRARY__ +#include + +_syscall3(int,execve,const char *,file,char **,argv,char **,envp) + diff --git a/mm/memory.c b/mm/memory.c index 7f9325e..8f7eec9 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -153,6 +153,26 @@ static unsigned long put_page(unsigned long page,unsigned long address) { return page; } +unsigned long put_dirty_page(unsigned long page, unsigned long address) { + unsigned long tmp, *page_table; + + if (page < LOW_MEM || page >= HIGH_MEMORY) + printk("Trying to put page %p at %p\n",page,address); + if (mem_map[(page-LOW_MEM)>>12] != 1) + printk("mem_map disagrees with %p at %p\n",page,address); + page_table = (unsigned long *) ((address>>20) & 0xffc); + if ((*page_table)&1) + page_table = (unsigned long *) (0xfffff000 & *page_table); + else { + if (!(tmp=get_free_page())) + return 0; + *page_table = tmp|7; + page_table = (unsigned long *) tmp; + } + page_table[(address>>12) & 0x3ff] = page | (PAGE_DIRTY | 7); + return page; +} + void get_empty_page(unsigned long address) { unsigned long tmp; if (!(tmp = get_free_page()) || !put_page(tmp, address)) { @@ -248,15 +268,17 @@ void do_no_page(unsigned long error_code,unsigned long address) { return; } - if (!share_page(current->executable, tmp)) + if (share_page(current->executable, tmp)) return; if (!(page = get_free_page())) oom(); block = 1 + tmp / BLOCK_SIZE; + printk("do_no_page, tmp, block is %d, %d\n", tmp, block); + - for (i = 0; i < 4; i++) { + for (i = 0; i < 4; block++, i++) { nr[i] = bmap(current->executable, block); } bread_page(page, current->executable->i_dev, nr); -- Gitee