Discussion:
Examining values returned from lstat64()
Toby Riddell
2011-09-05 21:25:47 UTC
Permalink
Hi all,

I'm trying to examine the values returned to a process from a
lstat64() call. I'm using the common pattern: in the
syscall::foo:entry probe save the address the process supplies for
returning results to it, and in the syscall::foo:return probe examine
the results. I've looked at several threads on this mailing list and
seen this technique used but for the life of me I cannot get it to
work for me.

I've reduced it to a simple test case using the following C program:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main() {
struct stat64 buffer;
int status;
status = lstat64("/home/triddel/stat.c", &buffer);
}

And this dtrace script:

#!/usr/sbin/dtrace -s

#pragma D option quiet

syscall::lstat64:entry
/ pid == $target /
{
self->path = arg0; /* char * */
self->stat = arg1; /* struct stat64 * */
}

syscall::lstat64:return
/ self->stat && pid == $target /
{
this->path = copyinstr(self->path);
printf("Path: %s\n", this->path);

this->stat = (struct stat64 *) copyin( (uintptr_t) self->stat,
sizeof(struct stat64) );
printf("File mode: %d\n", this->stat->st_mode);
printf("File size: %d\n", this->stat->st_size);
printf("File inode: %d\n", this->stat->st_ino);
printf("File uid: %d\n", this->stat->st_uid);

self->stat = 0;
}

The results are:

$ sudo dtrace -s process_filesize.d -c ./stat
Path: /home/triddel/stat.c
File mode: 63676
File size: 0
File inode: 0
File uid: 33188
File fstype:

File size is definitely not zero, and nor is the inode. Clearly the
technique is working for the file path but it seems I'm doing
something incorrect when accessing members of the stat64 struct.

Can someone point me to what I'm doing wrong?

Thanks.

Toby
Casper.Dik-QHcLZuEGTsvQT0dZR+
2011-09-06 07:48:23 UTC
Permalink
Post by Toby Riddell
$ sudo dtrace -s process_filesize.d -c ./stat
Path: /home/triddel/stat.c
File mode: 63676
File size: 0
File inode: 0
File uid: 33188
File size is definitely not zero, and nor is the inode. Clearly the
technique is working for the file path but it seems I'm doing
something incorrect when accessing members of the stat64 struct.
Can someone point me to what I'm doing wrong?
There are three different stat structures in the kernel:

- stat32
- stat64 (native)
- stat64_32 (for 32 bit large file environment)
Post by Toby Riddell
::print -a "struct stat64"
0 {
0 st_dev
8 st_ino
10 st_mode
14 st_nlink
18 st_uid
1c st_gid
20 st_rdev
28 st_size
30 st_atim {
30 tv_sec
38 tv_nsec
}
40 st_mtim {
40 tv_sec
48 tv_nsec
}
50 st_ctim {
50 tv_sec
58 tv_nsec
}
60 st_blksize
68 st_blocks
70 st_fstype
}
Post by Toby Riddell
::print -a "struct stat64_32"
0 {
0 st_dev
4 st_pad1
10 st_ino
18 st_mode
1c st_nlink
20 st_uid
24 st_gid
28 st_rdev
2c st_pad2
38 st_size
40 st_atim {
40 tv_sec
44 tv_nsec
}
48 st_mtim {
48 tv_sec
4c tv_nsec
}
50 st_ctim {
50 tv_sec
54 tv_nsec
}
58 st_blksize
60 st_blocks
68 st_fstype
78 st_pad4
}
Toby Riddell
2011-09-06 08:31:30 UTC
Permalink
Thanks a lot, Casper - this morning I was using tracemem and gradually
approaching the conclusion that there was a 32 vs. 64-bit issue but
this has helped accelerate my understanding.

However, when I modify my dtrace script to use stat64_32 I get
'invalid alignment' when I try to print the st_size member of the
struct:

syscall::lstat64:return
/ self->stat && pid == $target /
{
this->path = copyinstr(self->path);
printf("Path: %s\n", this->path);

this->stat = (struct stat64_32 *) copyin( (uintptr_t)
self->stat, sizeof(struct stat64_32) );
printf("File mode: %d\n", this->stat->st_mode);
printf("File size: %d\n", this->stat->st_size);
printf("File inode: %d\n", this->stat->st_ino);
printf("File uid: %d\n", this->stat->st_uid);

tracemem( copyin(self->stat, sizeof(struct stat64_32)),
sizeof(struct stat64_32) );

self->stat = 0;
}

$ sudo dtrace -C -s ./process_filesize.d -c ./stat
cc1: warning: /dev/fd/7 is shorter than expected
<command-line>: warning: "__STDC__" redefined
dtrace: error on enabled probe ID 2 (ID 49454:
syscall::lstat64:return): invalid alignment (0xfffffeefdc9702ec) in
action #5 at DIF offset 12

Commenting out the printf()s so that I get the output from tracemem:

0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
0: 00 00 54 01 00 00 00 00 00 00 00 00 00 00 00 00 ..T.............
10: bc f8 00 00 00 00 00 00 a4 81 00 00 01 00 00 00 ................
20: 21 27 00 00 10 27 00 00 00 00 00 00 00 00 00 00 !'...'..........
30: 00 00 00 00 a8 00 00 00 00 00 00 00 7a cd 65 4e ............z.eN
40: 10 b9 7a 34 1f 3e 65 4e b0 d3 b7 2d 1f 3e 65 4e ..z4.>eN...-.>eN
50: 50 24 c2 2d 00 20 00 00 02 00 00 00 00 00 00 00 P$.-. ..........
60: 6c 6f 66 73 00 00 00 00 00 00 00 00 00 00 00 00 lofs............
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................

The size of the file (hex a8) appears at byte 34 which doesn't agree
with any of the different structs... (in struct stat32 it's at offset
30).

So I'm still a bit stumped.
Post by Toby Riddell
$ sudo dtrace -s process_filesize.d -c ./stat
Path: /home/triddel/stat.c
File mode: 63676
File size: 0
File inode: 0
File uid: 33188
File size is definitely not zero, and nor is the inode. Clearly the
technique is working for the file path but it seems I'm doing
something incorrect when accessing members of the stat64 struct.
Can someone point me to what I'm doing wrong?
       - stat32
       - stat64 (native)
       - stat64_32 (for 32 bit large file environment)
Post by Toby Riddell
::print -a "struct stat64"
0 {
   0 st_dev
   8 st_ino
   10 st_mode
   14 st_nlink
   18 st_uid
   1c st_gid
   20 st_rdev
   28 st_size
   30 st_atim {
       30 tv_sec
       38 tv_nsec
   }
   40 st_mtim {
       40 tv_sec
       48 tv_nsec
   }
   50 st_ctim {
       50 tv_sec
       58 tv_nsec
   }
   60 st_blksize
   68 st_blocks
   70 st_fstype
}
Post by Toby Riddell
::print -a "struct stat64_32"
0 {
   0 st_dev
   4 st_pad1
   10 st_ino
   18 st_mode
   1c st_nlink
   20 st_uid
   24 st_gid
   28 st_rdev
   2c st_pad2
   38 st_size
   40 st_atim {
       40 tv_sec
       44 tv_nsec
   }
   48 st_mtim {
       48 tv_sec
       4c tv_nsec
   }
   50 st_ctim {
       50 tv_sec
       54 tv_nsec
   }
   58 st_blksize
   60 st_blocks
   68 st_fstype
   78 st_pad4
}
Casper.Dik-QHcLZuEGTsvQT0dZR+
2011-09-06 08:51:27 UTC
Permalink
Post by Toby Riddell
The size of the file (hex a8) appears at byte 34 which doesn't agree
with any of the different structs... (in struct stat32 it's at offset
30).
::print -at "struct stat64_32"
0 struct stat64_32 {
0 dev32_t st_dev
4 int32_t [3] st_pad1
10 ino64_t st_ino
18 mode32_t st_mode
1c nlink32_t st_nlink
20 uid32_t st_uid
24 gid32_t st_gid
28 dev32_t st_rdev
2c int32_t [2] st_pad2
34 off64_t st_size <--- offset 0x34
3c timestruc32_t st_atim {
3c time32_t tv_sec
40 int32_t tv_nsec
}
44 timestruc32_t st_mtim {
44 time32_t tv_sec
48 int32_t tv_nsec
}
4c timestruc32_t st_ctim {
4c time32_t tv_sec
50 int32_t tv_nsec
}
54 int32_t st_blksize
58 blkcnt64_t st_blocks
60 char [16] st_fstype
70 int32_t [8] st_pad4
}


Not sure about the why the code doesn't work.

Casper
Toby Riddell
2011-09-06 09:53:13 UTC
Permalink
I managed to get it working by hard-coding the offset (ugh!):

syscall::lstat64:return
/ self->stat && pid == $target /
{
this->path = copyinstr(self->path);
printf("Path: %s\n", this->path);

this->stat = copyin( self->stat + 0x34, sizeof(long long) );
printf("File size: %lld\n", *((long long *)(this->stat)));

self->stat = 0;
}
Post by Casper.Dik-QHcLZuEGTsvQT0dZR+
Post by Toby Riddell
The size of the file (hex a8) appears at byte 34 which doesn't agree
with any of the different structs... (in struct stat32 it's at offset
30).
::print -at "struct stat64_32"
0 struct stat64_32 {
   0 dev32_t st_dev
   4 int32_t [3] st_pad1
   10 ino64_t st_ino
   18 mode32_t st_mode
   1c nlink32_t st_nlink
   20 uid32_t st_uid
   24 gid32_t st_gid
   28 dev32_t st_rdev
   2c int32_t [2] st_pad2
   34 off64_t st_size          <--- offset 0x34
   3c timestruc32_t st_atim {
       3c time32_t tv_sec
       40 int32_t tv_nsec
   }
   44 timestruc32_t st_mtim {
       44 time32_t tv_sec
       48 int32_t tv_nsec
   }
   4c timestruc32_t st_ctim {
       4c time32_t tv_sec
       50 int32_t tv_nsec
   }
   54 int32_t st_blksize
   58 blkcnt64_t st_blocks
   60 char [16] st_fstype
   70 int32_t [8] st_pad4
}
Not sure about the why the code doesn't work.
Casper
Jonathan Adams
2011-09-06 16:55:15 UTC
Permalink
Post by Toby Riddell
syscall::lstat64:return
/ self->stat && pid == $target /
{
this->path = copyinstr(self->path);
printf("Path: %s\n", this->path);
this->stat = copyin( self->stat + 0x34, sizeof(long long) );
printf("File size: %lld\n", *((long long *)(this->stat)));
self->stat = 0;
}
The problem is that dtrace is trying to prevent unaligned loads, mainly
because SPARC faults on them; I'm not entirely sure, but I believe
amd64 wouldn't have a problem with performing the load.

If you want to make this less hardcoded, you could do:

this->stat = copyin(self->stat + offsetof(struct stat64_32, st_size),
sizeof (long long));

Cheers,
- jonathan
Post by Toby Riddell
Post by Casper.Dik-QHcLZuEGTsvQT0dZR+
Post by Toby Riddell
The size of the file (hex a8) appears at byte 34 which doesn't agree
with any of the different structs... (in struct stat32 it's at offset
30).
::print -at "struct stat64_32"
0 struct stat64_32 {
   0 dev32_t st_dev
   4 int32_t [3] st_pad1
   10 ino64_t st_ino
   18 mode32_t st_mode
   1c nlink32_t st_nlink
   20 uid32_t st_uid
   24 gid32_t st_gid
   28 dev32_t st_rdev
   2c int32_t [2] st_pad2
   34 off64_t st_size          <--- offset 0x34
   3c timestruc32_t st_atim {
       3c time32_t tv_sec
       40 int32_t tv_nsec
   }
   44 timestruc32_t st_mtim {
       44 time32_t tv_sec
       48 int32_t tv_nsec
   }
   4c timestruc32_t st_ctim {
       4c time32_t tv_sec
       50 int32_t tv_nsec
   }
   54 int32_t st_blksize
   58 blkcnt64_t st_blocks
   60 char [16] st_fstype
   70 int32_t [8] st_pad4
}
Not sure about the why the code doesn't work.
Casper
_______________________________________________
dtrace-discuss mailing list
Toby Riddell
2011-09-07 07:38:21 UTC
Permalink
That's definitely an improvement - thanks.
Post by Jonathan Adams
Post by Toby Riddell
syscall::lstat64:return
/ self->stat && pid == $target /
{
        this->path = copyinstr(self->path);
        printf("Path: %s\n", this->path);
        this->stat =  copyin( self->stat + 0x34, sizeof(long long) );
        printf("File size: %lld\n", *((long long *)(this->stat)));
        self->stat = 0;
}
The problem is that dtrace is trying to prevent unaligned loads, mainly
because SPARC faults on them; I'm not entirely sure, but I believe
amd64 wouldn't have a problem with performing the load.
       this->stat = copyin(self->stat + offsetof(struct stat64_32, st_size),
           sizeof (long long));
Cheers,
- jonathan
Post by Toby Riddell
Post by Casper.Dik-QHcLZuEGTsvQT0dZR+
Post by Toby Riddell
The size of the file (hex a8) appears at byte 34 which doesn't agree
with any of the different structs... (in struct stat32 it's at offset
30).
::print -at "struct stat64_32"
0 struct stat64_32 {
   0 dev32_t st_dev
   4 int32_t [3] st_pad1
   10 ino64_t st_ino
   18 mode32_t st_mode
   1c nlink32_t st_nlink
   20 uid32_t st_uid
   24 gid32_t st_gid
   28 dev32_t st_rdev
   2c int32_t [2] st_pad2
   34 off64_t st_size          <--- offset 0x34
   3c timestruc32_t st_atim {
       3c time32_t tv_sec
       40 int32_t tv_nsec
   }
   44 timestruc32_t st_mtim {
       44 time32_t tv_sec
       48 int32_t tv_nsec
   }
   4c timestruc32_t st_ctim {
       4c time32_t tv_sec
       50 int32_t tv_nsec
   }
   54 int32_t st_blksize
   58 blkcnt64_t st_blocks
   60 char [16] st_fstype
   70 int32_t [8] st_pad4
}
Not sure about the why the code doesn't work.
Casper
_______________________________________________
dtrace-discuss mailing list
Chris Ridd
2011-09-06 09:59:29 UTC
Permalink
Post by Casper.Dik-QHcLZuEGTsvQT0dZR+
Not sure about the why the code doesn't work.
Does passing -d32 to dtrace help? I get struct size/offset errors trying to analzye 32-bit binaries without this, though I'm not sure if stat64 would be affected.

Chris
Casper.Dik-QHcLZuEGTsvQT0dZR+
2011-09-06 11:15:02 UTC
Permalink
Post by Chris Ridd
Does passing -d32 to dtrace help? I get struct size/offset errors trying to analzye 32-bit binarie
s without this, though I'm not sure if stat64 would be affected.

struct stat64_32 is the representation of the 32 bit "struct stat64" in
a 64 bit application (specifically, time stamps are 32 bits and not
64 bits as is the case in the native stat64)

Casper
Loading...