test case for gs clear problem

Ulrich Drepper (drepper@cygnus.com)
26 Oct 1999 21:17:44 -0700


I'm hunting this problem for ages but never took the time to build a
test program which does not use libpthread. Finally here is one. I
would be grateful if somebody could fix this since it would allow a
much better thread implementation.

If you run the appended program (should compile on all glibc 2.1
systems, binary on request, leave out optimization) you should see
that the program happily runs for a few rounds. The %gs registers has
the expected contents and accessing memory works fine.

Then suddenly the value is zero. This happens after a system call. I
observed it in this program after the waitpid call, in the real
program with this problem it is after a poll() syscall. Clearing the
register value is not ok since the segment must be fine. If not, the
kernel somehow damaged the segment table.

Probably related is the problem that in signal handlers %gs also is
unset from time to time. But this can be corrected by reloading the
value from the signal context. At least for now, it should be fixed
as well.

I really cannot think that it is a userlevel problem since the
functions call in the look really are nothing but system call
wrappers.

I've tested this program on 2.3.14 but, as mentioned, I'm hunting this
for ages.

-- 
---------------.      drepper at gnu.org  ,-.   1325 Chesapeake Terrace
Ulrich Drepper  \    ,-------------------'   \  Sunnyvale, CA 94089 USA
Cygnus Solutions `--' drepper at cygnus.com   `------------------------

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #include <assert.h> #include <fcntl.h> #include <sched.h> #include <signal.h> #include <unistd.h> #include <sys/poll.h> #include <sys/wait.h>

char seg[4096]; char buf[16386];

#define show_gs(c) \ { \ char buf[10]; \ int gs; \ asm("movl %%gs,%0":"=g"(gs)); \ buf[0]='g'; \ buf[1]='s'; \ buf[2]=c; \ buf[3]='='; \ buf[4]=((gs>>4)&0xf)>9?'a'+((gs>>4)&0x0f)-10:'0'+((gs>>4)&0xf); \ buf[5]=(gs&0xf)>9?'a'+(gs&0xf)-10:'0'+(gs&0xf); \ buf[6]='\n'; \ write(2,buf,7); \ }

struct modify_ldt_ldt_s { unsigned int entry_number; unsigned long int base_addr; unsigned int limit; unsigned int seg_32bit:1; unsigned int contents:2; unsigned int read_exec_only:1; unsigned int limit_in_pages:1; unsigned int seg_not_present:1; unsigned int useable:1; unsigned int empty:25; };

static int fn (void *arg) { sleep (1); _exit (0); }

void catch (int sig) { unsigned char c;

#if 0 asm ("movb %%gs:0,%b0" : "=r" (c));

assert (c == 0); #endif }

int main (void) { int fd = open ("/dev/null", O_RDONLY); struct pollfd ufd; struct modify_ldt_ldt_s ldt_entry; struct sigaction sa;

ufd.fd = fd; ufd.events = POLLIN;

ldt_entry.entry_number = 0; ldt_entry.base_addr = (long int) seg; ldt_entry.limit = sizeof (seg); ldt_entry.seg_32bit = 1; ldt_entry.contents = 0; ldt_entry.read_exec_only = 0; ldt_entry.limit_in_pages = 0; ldt_entry.seg_not_present = 0; ldt_entry.useable = 1; ldt_entry.empty = 0;

modify_ldt (1, &ldt_entry, sizeof (ldt_entry));

asm volatile ("movw %w0,%%gs" : : "r" (7));

sa.sa_handler = catch; sa.sa_flags = 9; sigemptyset (&sa.sa_mask); sigaddset (&sa.sa_mask, SIGCHLD); sigaction (SIGCHLD, &sa, NULL);

while (1) { unsigned char c; int s; int p;

show_gs ('1');

p = clone (fn, buf + sizeof buf, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | SIGRTMIN, NULL);

show_gs ('2');

poll (&ufd, 1, 2000);

show_gs ('3');

asm ("movb %%gs:0,%b0" : "=r" (c));

show_gs ('4');

if (c != 0) abort ();

waitpid (p, &s, WNOHANG | __WCLONE);

show_gs ('5'); }

return 0; }

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/