x86-32: can't create call gate in LDT?

From: Alphabet Hotel
Date: Tue May 31 2022 - 09:12:15 EST


I was looking at emulating the lcall7 syscall interface in userspace
but it seems I can't even ask for a call gate. I did get it to work by
using a regular code segment for entry 0 and mapping my lcall7 handler
at offset 0 but now I'm in a different code segment and GCC is lost.
Could the ability to use call gates be added to the kernel? The patch
looks fairly easy and if it only allows calls into the existing CS it
should be safe. My test program works as-is but it's a hack and it
would require switching segments on every syscall.

#define _GNU_SOURCE
#include <stdio.h>
#include <err.h>
#include <sys/syscall.h>
#include <asm/ldt.h>

static int ret = 1, val = 0;

static void __attribute__ ((regparm(1))) lcall7(int call_nr) {
val = call_nr; // remaining args are on the stack
asm volatile ("xor %eax,%eax;"
"lret;" // far return is required
);
}

int main() {
struct user_desc desc = {
.entry_number = 0,
.base_addr = (unsigned long)&lcall7,
.limit = 1, // one page
.limit_in_pages = 1,
.seg_32bit = 1,
.contents = 2, // non-conforming code
};

if (syscall(__NR_modify_ldt, 0x11, &desc, sizeof(desc)))
err(1, "modify_ldt");

asm volatile ("lcall $7,$0;"
: "=a" (ret)
: "a" (42)
);
if (!ret && val == 42)
printf("lcall7 worked\n");
}