-
Notifications
You must be signed in to change notification settings - Fork 14
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
Excessive memory consumption #90
Comments
I managed to reduce the memory consumption dramatically by splitting section into chunks of 1 MiB. E.g. currently I am not able to analyze Doing something like for chunk in data.chunks(1024 * 1024) {
let insns = cs
.disasm_all(chunk, 0) reduces memory consumption down to ~500 MiB and finishes successfully. A fairly dumb and straightforward solution. The only possible downside I can think of: some instructions may fall on a boundary of the slice and be split in half. Which means they will be skipped as invalid. |
I'm still quite surprised about this memory consumption issue, but if a chunking approach helps, then great! If you overlap the chunks by a little bit, would that avoid the pitfall of missing instructions that fall on a boundary? I suppose there is a possibility then that maybe an incomplete instruction will be mis-parsed? Alternatively, maybe there's a way to figure out the byte offset and size of the final decoded instruction, and start the next chunk at a known boundary? |
Well, it contains a large amount of detailed information for every instruction. Disabling the detail mode reduces memory consumption significantly, but we lose ability to identify the group obviously.
Maybe. Shrinking down to 4 KiB chunks I am able to see some discrepancy.
I think it is technically possible. |
Actually yes, let data = sect.data().expect("couldn't get section data");
let mut offset = 0;
loop {
let rest = &data[offset..];
if rest.is_empty() {
break;
}
let insns = cs
.disasm_count(rest, 0, 1)
.expect("couldn't disassemble section");
for insn in insns.iter() {
offset += insn.bytes().len();
let Ok(detail) = cs.insn_detail(insn) else {
continue;
};
for group_code in detail.groups() {
if seen_groups.insert(group_code.0) {
if let Some(mnemonic) = insn.mnemonic() {
if let Some(desc) = describe_group(&group_code.0) {
println!("{} ({})", desc, mnemonic);
}
}
}
}
}
} Reading instructions one-by-one works like a charm. In fact it is generally 3x faster than current 0.6.1 release, even for small binaries, on my machine. |
I am also very surprised that going one-by-one is faster too, but I'll take it! Maybe there are arrays that are realloc'ed with inefficient copying around, or something. It might be a little more helpful to |
I guess so. Because performance only goes down with increased count of instructions requested via
Maybe, but I don't really know in which situation it can fail. Invalid instructions are simply skipped as we know. |
To summarize, I am unable to find an explanation of reasons So I think to push the PR as the current implementation seems good according to my testing. |
Btw there is Maybe it is worth trying to call it directly via |
Well, my test shows that So for now I'd better go with the current PR. |
Thank you for investigating and documenting your findings! |
Btw, I noticed that simply updating dependencies to the latest versions improves the performance a bit. |
I posted too late 🙃 |
As reported in the discussion of #66, it seems that this program sometimes eats up an amount of memory that is totally out of line with what it should be doing. E.g., "Analyzing a 123MB binary OOMs my 24GB RAM machine."
I don't use this tool myself anymore and unfortunately I'm not in a position to spend time on it, but maybe the proposal in #47 would help with addressing this. The approach taken by the code for this program is about as straightforward as it can get, so I feel like there must be a problem with one of the underlying libraries (or their Rust bindings).
CC @rowanworth @Ristovski @oldherl
The text was updated successfully, but these errors were encountered: