I'm trying to memset the data at some address in a gdb session.
Lets say it is initially filled with 1's and I'm trying to overwrite it with 0's.
(gdb) set $i = (int*)malloc(sizeof(int))
(gdb) set *$i = -1
(gdb) x/t $i
0x76d8550: 11111111111111111111111111111111
The data is not modified at all when I:
Make a gdb.Value out of memset function pointer and call it in Python with the right address;
Run ctypes' memset(), passing it the right address.
(gdb) pi memset = gdb.parse_and_eval("(void*(*)(void*,int,size_t))memset").dereference()
(gdb) pi str(memset)
'{void *(void *, int, size_t)} 0x7fffe992e760 <memset>'
(gdb) pi
>>> i = 0x76d8550
>>> memset(i,0,4)
<gdb.Value object at 0x7fdc5c0fbef0>
>>> gdb.execute("x/t $i")
0x76d8550: 11111111111111111111111111111111
>>> import ctypes
>>> ctypes.memset(i,0,4)
124618064
>>> gdb.execute("x/t $i")
0x76d8550: 11111111111111111111111111111111
The data is modified as expected when I:
- Evaluate a string with the complete memset() expression using gdb.parse_and_eval().
>>> gdb.parse_and_eval("(void*)memset({},0,4)".format(i))
<gdb.Value object at 0x7fdb9eef58b0>
>>> gdb.execute("x/t $i")
0x76d8550: 00000000000000000000000000000000
Any explanation on why the 1st two options aren't working?
Thanks
Judging by the addresses you get printed you are probably running on Linux/x86_64, and possibly using GLIBC as your standard
C
library. If so ......
memset
is complicated.First, there are two separate implementations of
memset
-- a minimal one inld-linux.so
and a full-function one insidelibc.so.6
.Second, the full implementation in
libc.so.6
is a GNU IFUNC, which means that it doesn't itself write to memory, it just returns the address of the function that should be used to write to memory on a given processor.Lastly, as sbssa commented,
ctype.memset()
can't possibly work, because that's amemset
that is within the GDB itself, not thememset
in the inferior (being debugged) process. By callingctypes.memset(i,0,4)
you are corrupting a random location within GDB. This could result in anything, starting from "no effect" (if the corrupted address was unused but valid) to having the expression immediately crash (if the "to be corrupted" address is invalid) to a random crash in GDB later (if that corrupted address is actually used by GDB for something).Putting this all together:
Compiled with
gcc -g x.c
, and running under GDB on Fedora 38x86_64
:Here you can see that GDB got the wrong
memset
(the minimal implementation). It would still work, but is suboptimal and may have other restrictions -- it was never intended to be used outside ofld-linux
itself. For example, it may assume that the buffer is 8-byte aligned, or that the size is at least 8 bytes, etc.What about the real
memset
that is called inmain
?Note that this is the IFUNC` I was talking about.
The
__memset_avx2_unaligned
is the actualmemset
implementation selected for this host (out of several possible; the other possibilities in this build of GLIBC are:__memset_erms
,__memset_avx2_unaligned_erms
,__memset_evex_unaligned
,__memset_evex_unaligned_erms
).Note that even though we've already returned from "memset", the value of
jj
is still-1
:P.S. Why is the value changed by
__memset_avx2_unaligned_erms()
and not by__memset_avx2_unaligned()
?Because the latter uses "tail call" to the latter: