high memory addresses ("top" of heap)
| unmapped memory |
|-----------------|
| |
| top chunk |
| (mapped, but |
| not yet |
| allocated) |
| |
|-----------------|
| allocated chunk |
|-----------------|
| freed chunk |
|-----------------|
| allocated chunk |
| |
|-----------------|
| allocated chunk |
|-----------------|
| freed chunk |
|-----------------|
| tcache |
|-----------------|
low memory addresses ("bottom") of heap
That likely indicates a heap buffer overflow. If a call to malloc() doesn't find a freed chunk, it will split the top chunk in two and return a pointer to the bottom portion. If you then write past the end of the returned allocation, you clobber the metadata of the top chunk, and get errors like the ones in the article.With the exploit mitigations built into modern Linux and glibc, it's a lot of work to go from here to arbitrary code execution; but it very well may be possible, depending on exactly how much control the attacker has over what atop does. The attacker can probably trigger the heap buffer overflow multiple times by spawning multiple processes, and if the length and contents of the heap buffer overwrite are attacker-controlled, they can probably play some games to overwrite any data stored in the heap. If that's true, the only thing preventing full arbitrary-code-execution is ASLR; there are many clever ways to get around that, but it's often quite difficult and may or may not be possible here.
This year's LACTF had a challenge with essentially this exact setup. My solution writeup is a good example of what it takes to defeat the exploit mitigations and turn a heap buffer overflow into an RCE: https://jonathankeller.net/ctf/lamp/