Openwall GNU/*/Linux - a small security-enhanced Linux distro for servers
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Date: Sat, 16 Mar 2013 01:25:04 +0100 (CET)
From: Pavel Kankovsky <>
Subject: Re: strace

On Fri, 15 Mar 2013, Pavel Kankovsky wrote:

> On Fri, 15 Mar 2013, Dmitry V. Levin wrote:
>>  The change of orig_eax offset means that there was a change in the ptrace
>>  ABI between 2.6.18 and 2.6.32, and all its users (e.g. strace and gdb)
>>  should be rebuilt.
> Things appear to be a little more complicated.
> As far as I can tell, PTRACE_GETREGS returns data in struct pt_regs 
> layout on 2.6.18 and in struct user_regs_struct layout on 2.6.32. Those 
> two structs are compatible on 2.6.32 but they are different on 2.6.18 
> (i386 arch; x86-64 is probably unaffected).

Uhh... I am sorry. Let me retract this statement, please. I was not 
thinking clearly (note to myself: do not write & send any important emails 
after 2am) and I misinterpreted the code in 2.6.18 (I quote vanilla 
here, RH added some irrelevant stuff):

static unsigned long getreg(struct task_struct *child,
 	unsigned long regno)
 	unsigned long retval = ~0UL;

 	switch (regno >> 2) {
 		case FS:
 			retval = child->thread.fs;
 		case GS:
 			retval = child->;
 		case DS:
 		case ES:
 		case SS:
 		case CS:
 			retval = 0xffff;
 			/* fall through */
 			if (regno > GS*4)
 				regno -= 2*4;
 			regno = regno - sizeof(struct pt_regs);
 			retval &= get_stack_long(child, regno);
 	return retval;

I did not pay enough attention to "if (regno > GS*4) regno -= 2*4".
That command skips two elements that are present in user_regs_struct
but are missing in pt_regs:

pt_regs (2.6.18):         user_regs_struct (2.6.32):
   long ebx;                 unsigned long   bx;
   long ecx;                 unsigned long   cx;
   long edx;                 unsigned long   dx;
   long esi;                 unsigned long   si;
   long edi;                 unsigned long   di;
   long ebp;                 unsigned long   bp;
   long eax;                 unsigned long   ax;
   int  xds;                 unsigned long   ds;
   int  xes;                 unsigned long   es;
                             unsigned long   fs;
                             unsigned long   gs;
   long orig_eax;            unsigned long   orig_ax;
   long eip;                 unsigned long   ip;
   int  xcs;                 unsigned long   cs;
   long eflags;              unsigned long   flags;
   long esp;                 unsigned long   sp;
   int  xss;                 unsigned long   ss;

(For some unfathomable reasons, register names in user_regs_struct differ
between kernel headers and Glibc but the layout stays the same.)

There were some changes: GS was migrated to struct pt_regs in 2.6.20
and the code was reengineered completely in 2.6.25. But it appears to me
(now) that its interface has been stable.

I made a small test program PTRACE_GETREGS and I get the expected results 
(namely orig_eax == 0x25 == SYS_kill) on RHEL 2.6.18 and on 2.6.32.

  0: ebx      0x0000372a
  1: ecx      0x00000002
  2: edx      0x009c4ff4
  3: esi      0x00867ca0
  4: edi      0x00000000
  5: ebp      0xbffa1b08
  6: eax      0x00000000
  7: ds       0x0000007b
  8: es       0x0000007b
  9: fs       0x00000000
10: gs       0x00000033
11: orig_eax 0x00000025  <---- SYS_kill
12: eip      0x006bb402
13: cs       0x00000073
14: eflags   0x00000246
15: esp      0xbffa1a88
16: ss       0x0000007b
17: xxx      0xdeafbeef

Pavel Kankovsky aka Peak                          / Jeremiah 9:21        \
"For death is come up into our MS Windows(tm)..." \ 21st century edition /
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/user.h>
#include <sys/ptrace.h>
#include <asm/ptrace.h>
#include <sys/signal.h>

#define REGN (FRAME_SIZE+1)

const char *reg_names[REGN] = {
  /*  0 */ "ebx", "ecx", "edx", "esi", "edi", "ebp", "eax", "ds",
  /*  8 */ "es", "fs", "gs", "orig_eax", "eip", "cs", "eflags", "esp",
  /* 16 */ "ss", "xxx"

  pid_t pid = fork();
  if (pid == -1) {
  else if (pid == 0) {
    int r;
    r = ptrace(PTRACE_TRACEME, 0, NULL, NULL);
    if (r == -1) {
    kill(getpid(), SIGINT);
  else {
    int r, i, status;
    unsigned long regs[REGN];
    r = waitpid(pid, &status, 0);
    if (r == -1) {
    for (i = 0; i < REGN; ++i)
      regs[i] = 0xdeafbeefUL;
    r = ptrace(PTRACE_GETREGS, pid, NULL, (void *) &regs);
    if (r == -1) {
    for (i = 0; i < REGN; ++i)
      printf("%2d: %-8s 0x%08x\n", i, reg_names[i], regs[i]);
    ptrace(PTRACE_KILL, pid, NULL, NULL);
  return 0;

Powered by blists - more mailing lists

Your e-mail address:

Powered by Openwall GNU/*/Linux - Powered by OpenVZ