Skip to contentSkip to conversation

syscalls-by-name (aka dynamic syscalls / vsyscalls)

RFC In review Benjamin LaHaise 3 participants 3 messages Jan 17, 2002 – Jan 18, 2002

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

RFC 1/1 syscalls-by-name (aka dynamic syscalls / vsyscalls)

The attached patch implements syscalls-by-name in an initial and rather crud method -- the x86isms need tidying up, but the technique is usable across platforms. Syscalls provided by name are made available to userland by means of a dynamic shared library that maps and provides a jumptable into a set of vsyscalls. The vsyscalls can then enter the kernel through whatever mechanism (the only means implemented right now is via a new syscall, sys_dynamic_syscall) the kernel choses. Version mismatch between the kernel and the library is prevented by means of a unique fingerprint the library checks on load against what the kernel provides via /dev/vsys.

/dev/vsys is a simple device node that allows user code to map the vsyscall area into their address space. This is different from the fixed address that x86-64 uses to allow UML emulation of vsyscalls, and prevent other problems that could crop up with the intrusion of unexpected code into user address space.

For a quick example on how a new syscall is declared, I added the following lines into fs/aio.c in the aio patch:

	#include <asm/vsyscall.h>
 	... rest of file ...
	add_dynamic_syscall(sys_io_setup);
	add_dynamic_syscall(sys_io_destroy);
	add_dynamic_syscall(sys_io_submit);
	add_dynamic_syscall(sys_io_cancel);
	add_dynamic_syscall(sys_io_wait);
	add_dynamic_syscall(sys_io_getevents);

Similarly, vsyscalls can be added by means of adding an attribute to a function declaration that puts the code into the VSYSCALL segment. This segment is linked at a virtual address just above the top of the default user stack, but is kept within the kernel image and mapped via /dev/vsys.

My current todos on the code are to clean it up so that the arch specific parts are better seperated, add symbol versions, and resurrect the sysenter / sysexit code as the means of entering the dynamic syscalls. There's also TSC based gettimeofday() code to finish off and add...

The patch is against 2.4.9-acsomething + aio + stuff, but should be pretty easy to merge into recent kernels. If anyone has any comments or tweaks, I'd like to hear about them. Cheers,

-ben

:r ~/syscall-by-name-A4.diff

Makefile +10 -4
diff -urN linux.wrk.orig/Makefile linux.wrk.diff/Makefile
@@ -236,7 +236,7 @@
 	drivers/sound/pndsperm.c \
 	drivers/sound/pndspini.c \
 	drivers/atm/fore200e_*_fw.c drivers/atm/.fore200e_*.fw \
-	.version .config* config.in config.old \
+	.uniquebytes .version .config* config.in config.old \
 	scripts/tkparse scripts/kconfig.tk scripts/kconfig.tmp \
 	scripts/lxdialog/*.o scripts/lxdialog/lxdialog \
 	.menuconfig.log \
@@ -283,6 +283,7 @@
 
 vmlinux: include/linux/version.h $(CONFIGURATION) init/main.o init/version.o linuxsubdirs
 	@$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" kallsyms
+	@$(MAKE) -C ulib
 
 .PHONY:	kallsyms
 
@@ -332,14 +333,17 @@
 
 linuxsubdirs: $(patsubst %, _dir_%, $(SUBDIRS))
 
-$(patsubst %, _dir_%, $(SUBDIRS)) : dummy include/linux/version.h include/config/MARKER
+$(patsubst %, _dir_%, $(SUBDIRS)) : dummy include/linux/compile.h include/config/MARKER
 	$(MAKE) CFLAGS="$(CFLAGS) $(CFLAGS_KERNEL)" -C $(patsubst _dir_%, %, $@)
 
 $(TOPDIR)/include/linux/version.h: include/linux/version.h
 $(TOPDIR)/include/linux/compile.h: include/linux/compile.h
 
 newversion:
-	. scripts/mkversion > .version
+	. scripts/mkversion > .tmpversion
+	@mv -f .tmpversion .version
+	rm -f .uniquebytes
+	dd if=/dev/urandom of=.uniquebytes bs=1 count=16
 
 include/linux/compile.h: $(CONFIGURATION) include/linux/version.h newversion
 	@echo -n \#define UTS_VERSION \"\#`cat .version` > .ver
@@ -357,9 +361,10 @@
 	   echo \#define LINUX_COMPILE_DOMAIN ; \
 	 fi >> .ver
 	@echo \#define LINUX_COMPILER \"`$(CC) $(CFLAGS) -v 2>&1 | tail -1`\" >> .ver
+	@echo \#define LINUX_UNIQUE_BYTES `hexdump -e '1/1 "0x%02x, "' .uniquebytes | sed -e 's/, \$$//g'` >>.ver
 	@mv -f .ver $@
 
-include/linux/version.h: ./Makefile include/linux/autoconf.h 
+include/linux/version.h: ./Makefile include/linux/autoconf.h
 	@echo \#define UTS_RELEASE \"$(KERNELRELEASE)\" > .ver
 	@echo \#define LINUX_VERSION_CODE `expr $(VERSION) \\* 65536 + $(PATCHLEVEL) \\* 256 + $(SUBLEVEL)` >> .ver
 	@echo '#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))' >>.ver
@@ -453,6 +458,7 @@
 	rm -f $(CLEAN_FILES)
 	rm -rf $(CLEAN_DIRS)
 	$(MAKE) -C Documentation/DocBook clean
+	$(MAKE) -C ulib clean
 
 mrproper: clean archmrproper
 	find . \( -size 0 -o -name .depend \) -type f -print | xargs rm -f
arch/i386/Makefile +1 -1
diff -urN linux.wrk.orig/arch/i386/Makefile linux.wrk.diff/arch/i386/Makefile
@@ -127,7 +127,7 @@
 zlilo: vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=zImage zlilo
 
-tmp:
+tmp: ulib
 	@$(MAKEBOOT) BOOTIMAGE=bzImage zlilo
 bzlilo: vmlinux
 	@$(MAKEBOOT) BOOTIMAGE=bzImage zlilo
arch/i386/kernel/Makefile +2 -2
diff -urN linux.wrk.orig/arch/i386/kernel/Makefile linux.wrk.diff/arch/i386/kernel/Makefile
@@ -23,8 +23,7 @@
 obj-y	:= process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
 		ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \
 		pci-dma.o i386_ksyms.o i387.o bluesmoke.o dmi_scan.o \
-		bootflag.o
-
+		bootflag.o vsysdata.o vunique.o dynamic_syscall.o
 
 ifdef CONFIG_PCI
 obj-y			+= pci-i386.o
@@ -53,3 +52,4 @@
 
 abi-machdep.o: $(abi-machdep-objs)
 	$(LD) -r -o $@ $(abi-machdep-objs)
+
arch/i386/kernel/dynamic_syscall.c +66 -0

66 lines not on this page · raw message

arch/i386/kernel/entry.S +6 -0
diff -urN linux.wrk.orig/arch/i386/kernel/entry.S linux.wrk.diff/arch/i386/kernel/entry.S
@@ -46,6 +46,7 @@
 #include <asm/segment.h>
 #define ASSEMBLY
 #include <asm/smp.h>
+#include <asm/unistd.h>
 
 EBX		= 0x00
 ECX		= 0x04
@@ -635,6 +636,11 @@
 # endif
 #endif
 
+	.rept __NR_sys_dynamic_syscall-(.-sys_call_table)/4
+		.long SYMBOL_NAME(sys_ni_syscall)
+	.endr
+	.long SYMBOL_NAME(sys_dynamic_syscall)
+
 	/* More patch resilient means of adding syscalls */
 	.rept 244-(.-sys_call_table)/4
 		.long SYMBOL_NAME(sys_ni_syscall)
arch/i386/kernel/vsysdata.c +11 -0
diff -urN linux.wrk.orig/arch/i386/kernel/vsysdata.c linux.wrk.diff/arch/i386/kernel/vsysdata.c
@@ -0,0 +1,11 @@
+/* vsysdata.c	- declarations for variables shared with the kernel
+ *
+ *	Items placed in .data.vsyscall have a kernel virtual address 
+ *	and are read/write from kernel space only.  The copy placed 
+ *	in .vsyscall_data are linked at a userspace address and are 
+ *	read only accessible from userland.
+ */
+#include <linux/vsyscall.h>
+
+union vsys_union user_vsys_cpudata[256] __attribute__((section(".vsyscall_data")));
+//asm(".globl vsys_cpudata ; bobbob = user_vsys_cpudata - vsyscall_text_begin ; vsys_cpudata = bobbob + VSYSCALL_text");
arch/i386/kernel/vunique.S +7 -0
diff -urN linux.wrk.orig/arch/i386/kernel/vunique.S linux.wrk.diff/arch/i386/kernel/vunique.S
@@ -0,0 +1,7 @@
+#include <linux/compile.h>
+
+	.section .first_vsyscall_text,"xa"
+	.globl	signature
+signature:
+	.byte	LINUX_UNIQUE_BYTES
+	.size	signature,.-signature
arch/i386/vmlinux.lds +41 -1

63 lines not on this page · raw message

arch/i386/vmlinux.lds.S +46 -0

62 lines not on this page · raw message

drivers/char/mem.c +77 -1

102 lines not on this page · raw message

include/asm-i386/a.out.h +3 -1
diff -urN linux.wrk.orig/include/asm-i386/a.out.h linux.wrk.diff/include/asm-i386/a.out.h
@@ -19,7 +19,9 @@
 
 #ifdef __KERNEL__
 
-#define STACK_TOP	TASK_SIZE
+#define VSYSCALL_SIZE		0x10000		/* 64KB for vsyscalls */
+#define STACK_GUARD_SIZE	0x02000		/* 8KB guard area */
+#define STACK_TOP	(TASK_SIZE - VSYSCALL_SIZE - STACK_GUARD_SIZE)
 
 #endif
 
include/asm-i386/unistd.h +2 -0
diff -urN linux.wrk.orig/include/asm-i386/unistd.h linux.wrk.diff/include/asm-i386/unistd.h
@@ -228,6 +228,8 @@
 #define __NR_getdents64		220
 #define __NR_fcntl64		221
 
+#define __NR_sys_dynamic_syscall	230
+
 /* gap here for now */
 #define	__NR_io_setup		244
 #define	__NR_io_destroy		245
include/asm-i386/vsyscall.h +41 -0

41 lines not on this page · raw message

include/linux/vsyscall.h +20 -0
diff -urN linux.wrk.orig/include/linux/vsyscall.h linux.wrk.diff/include/linux/vsyscall.h
@@ -0,0 +1,20 @@
+#ifndef _LINUX__VSYSCALL_H
+#define _LINUX__VSYSCALL_H
+
+struct vsys_cpudata {
+	unsigned long	context_switches;
+	unsigned long	tv_sec;
+	unsigned long	tsc_low, tsc_high;
+	unsigned long	cycles_per_sec;
+};
+
+union vsys_union {
+	struct vsys_cpudata	data;
+	char			pad[128];
+};
+
+extern union vsys_union	vsys_cpudata[256] __attribute__((section(".data.vsyscall")));
+
+#define vsys_data(cpu)	(&vsys_cpudata[cpu].data)
+
+#endif /*ndef _LINUX__VSYSCALL_H*/
ulib/Makefile +26 -0
diff -urN linux.wrk.orig/ulib/Makefile linux.wrk.diff/ulib/Makefile
@@ -0,0 +1,26 @@
+all: libkernel.so
+
+ASFLAGS=-D__KERNEL__ -D__ASSEMBLY__ -I../include -nostdlib -nostartfiles
+CFLAGS=-D__KERNEL__ -I../include -nostdlib -nostartfiles
+
+vsysaddr.S: ../System.map stub.S Makefile
+	rm -f vsysaddr.S
+	echo '#include "stub.S"' >vsysaddr.S
+	awk -- "/^bfff.* vsys_/ { print \"dynamic_syscall(\"\$$3 \",0x\" \$$1 \")\"; }" <../System.map >>vsysaddr.S
+
+vsysaddr.o: vsysaddr.S
+
+kso_init.o: ../include/linux/compile.h
+
+libkernel.so.1: vsysaddr.o kso_init.o
+	ld -E -symbolic -shared -soname=libkernel.so -o $@ vsysaddr.o kso_init.o
+
+libkernel.so: libkernel.so.1
+	ln -sf $< $@
+
+clean:
+	rm -f *.o libkernel.so myln libkernel.so.1 vsysaddr.S
+
+# test app
+myln: myln.c libkernel.so Makefile
+	cc -g -o myln myln.c -L. -lkernel
ulib/kso_init.c +48 -0

48 lines not on this page · raw message

ulib/myln.c +25 -0
diff -urN linux.wrk.orig/ulib/myln.c linux.wrk.diff/ulib/myln.c
@@ -0,0 +1,25 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+int main ()
+{
+	long ctx = 0;
+	extern long vsys_io_setup(long, long *);
+	unsigned char *bob = (void*)&vsys_io_setup;
+	long ret;
+	int i;
+	printf("%p\n", bob);
+	//printf("%p\n", mmap(0, 65536, PROT_READ | PROT_EXEC, MAP_SHARED,
+	//	open("/dev/vsys", O_RDONLY), 0));
+	//for (i=0; i<16; i++)
+	//	printf(" %02x\n", bob[i]);
+	//printf("\n");
+
+	ret = vsys_io_setup(100, &ctx);
+
+	printf("ret=%ld, ctx=0x%lx\n", ret, ctx);
+	return 0;
+}
ulib/stub.S +18 -0
diff -urN linux.wrk.orig/ulib/stub.S linux.wrk.diff/ulib/stub.S
@@ -0,0 +1,18 @@
+/* stub.S */
+#include <asm/segment.h>
+#include <asm/errno.h>
+
+	.text
+
+#define dynamic_syscall(x,a) \
+	.globl	x				;\
+	.align 16				;\
+	x:					;\
+		cmpb $0,libkernel_enosys	;\
+		jne 1f				;\
+		ljmp $__USER_CS, $a		;\
+	1:					;\
+		movl	$-ENOSYS,%eax		;\
+		ret				;\
+	.size	 x,.-x
+
#1 lore raw
Quoted 4 lines · Benjamin LaHaise

The attached patch implements syscalls-by-name [...]

If anyone has any comments [...]

Don't miss the wood for the trees.

Ciao, ET.