Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Display tcache for all threads, not just main thread #634

Closed
irontigran opened this issue Apr 4, 2021 · 3 comments · Fixed by #639
Closed

Display tcache for all threads, not just main thread #634

irontigran opened this issue Apr 4, 2021 · 3 comments · Fixed by #639

Comments

@irontigran
Copy link
Contributor

Is your feature request related to a problem? Please describe.

Currently, gef only correctly displays tcachebins for the main thread. (See screenshot at the bottom for example.)

Describe the solution you'd like

I'd like to upgrade gef to correctly display all tcachebins. I'd like to do this myself, but I think this will actually take a little bit of redesign, so I wanted to get some feedback before starting.

gef lists tcachebins as if they are associated with arenas - they are not. You can't get the address of a tcache from the information an arena gives you because each tcache is allocated (source). (The main thread's tcache is an exception. Since it's allocated early, we can guarantee that it will be at the same offset from the main arena. This is how gef finds tcaches currently.)

So the tcache command should be redesigned to be independent of arenas.

Potential implementation

It's relatively easy to get the address of each tcache using native gdb commands, though they aren't printed in an easy-to-parse output.

thread apply all p (void *) tcache

It gets a little bit more difficult inside gef. gdb.inferiors.threads will get you all the thread objects, but gdb.InferiorThread doesn't expose any tcache information. The dumb way is to switch between each thread and get the tcache for each one. Semi-plausible code might look like:

current_thread = gdb.selected_thread()
for thread in gdb.inferiors.thread():
    thread.switch()
    tcache_addr = gdb.parse_and_eval("tcache")
    # all the tcache parsing and printing
current_thread.switch() # restore current thread context

Interface

It seems reasonable to allow the user to 1) allow the user to print all the tcachebins, and 2) print only the tcache bin for a specific thread. The interesting question is what to do when the user types heap bins. Should we display all the tcachebins? Only the tcachebins for the current thread? None of the tcachebins? My initial preference would be to display the tcachebins for the current thread, but change the title from tcachebins for arena 0xdeadbeef to tcachebins for thread 1.

Additional context

Example of gef not displaying tcachebins correctly using heap-non-main-arena.out. I can find the thread's tcache manually, but gef won't display it.

tcache

@irontigran
Copy link
Contributor Author

irontigran commented Apr 5, 2021

I have working-but-messy code sitting over in b340eb7, with one significant problem.

To determine the exact layout of tcache_perthread_struct, we check glibc's version number - the struct changes slightly between 2.29 and 2.30. Most of the time this works - but according to this issue, RHEL backported the changes into glibc 2.28.

So if you're using Fedora (as I am), the change in tcache_perthread_struct happens two versions earlier, as opposed to using a non-Red Hat distro. (I tested this using Ubuntu 18.10 and 19.04, which use glibc 2.28 and 2.29 respectively. They both behave as expected.)

Options:

  1. We add somewhat kludgey code that checks glibc's version number and if the user is on an RH distro (Fedora, CentOS, etc.). This is both messy and something of a fool's errand, since I don't think we want to add a case for every RH distro. I suppose this could be a "good enough" solution if we make sure to support the most commonly used distros.
  2. We throw up our hands and ignore the problem, and add an entry to the FAQ.
  3. A third option I haven't thought of...?

@Grazfather
Copy link
Collaborator

Yeah, that's some hairiness for sure.

I don't like version hacks and such to exist in GEF, but maybe it makes sense here.

The glibc heap makes sense most of all to have these version hacks (do we even work with pre-tcache versions?). Maybe we should add these hacks, it would definitely be helpful for many.

Maybe these checks should exist as an optional gef-extra?

cc @hugsy

@irontigran
Copy link
Contributor Author

irontigran commented Apr 6, 2021

Sleeping on problems brings new insights!

It occurred to me that the tcache is itself a glibc chunk (source), so we can use the size of that chunk to tell us how large the tcache struct is. No checking glibc versions necessary. Diff is in faf50c7.

I'll continue working on cleaning up the code (and deleting now unneeded methods like GlibcArena.tcachebin); I hope to have a reasonable PR within a few days.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants