Skip to content

fix(AflppRedQueen): fix u64 I2S mutation and allow variable-length FnOperands#3741

Open
drewbarbs wants to merge 2 commits intoAFLplusplus:mainfrom
drewbarbs:fix-aflpprq
Open

fix(AflppRedQueen): fix u64 I2S mutation and allow variable-length FnOperands#3741
drewbarbs wants to merge 2 commits intoAFLplusplus:mainfrom
drewbarbs:fix-aflpprq

Conversation

@drewbarbs
Copy link
Copy Markdown

@drewbarbs drewbarbs commented Feb 26, 2026

Description

These are a couple of small fixes to the Aflpp RedQueen implementation:

  • fix AflppRedQueen mutator's u64 I2S replacement

    When the RQ mutator sees that a 8 byte comparison operand is equal to 8 bytes from the input, then it treats that as an I2S correspondence and pushes a mutation that replaces those input bytes with a big-endian encoding of the second comparison operand, repl (this process is done on both the original/byte-swapped versions of the relevant values, to handle either byte order)

    This commit fixes a bit shifting bug (probably typo) that breaks the replacement by repeating one byte and dropping the most significant byte, which makes RQ unable to solve the comparison.

  • fix AflppCmpLogFnOperands::new to allow v0/v1 with different lengths

    AflppCmpLogFnOperands has fixed length ([u8; 32]) storage for v0 and v1, and separate v0_len/v1_len fields. However, the rust constructor/setters only allow for v0/v1 to be initialized from slices that are exactly 32 bytes long, since copy_from_slice panics otherwise. So v0_len/v1_len can only be 32. The instrumentation in libafl_targets' cmplog.{h,c} can create log entries where the v0_len/v1_len values are anywhere between 0 and 32, and this change allows us to do the same from rust.

    Note: in AFLplusplus's cmplog instrumentation, v0_len/v1_len are always the same value. In AFLplusplus' implementation, __cmplog_rtn_hook_str can create log entries with v0_len != v1_len, which this change also allows.

Checklist

  • I have run ./scripts/precommit.sh and addressed all comments

@addisoncrump
Copy link
Copy Markdown
Member

awesome awesome awesome, I've been wanting this for a while. I'll give this a review once we've repaired the CI and you can once again rebase.

@domenukk
Copy link
Copy Markdown
Member

domenukk commented Apr 7, 2026

@addisoncrump Ci is green, can you take a look?

Copy link
Copy Markdown
Member

@addisoncrump addisoncrump left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some cleaning changes that should be done instead of the bug fix provided, since this was a code-copying error.

Comment on lines +1113 to +1120
cloned[buf_idx] = ((repl >> 48) & 0xff) as u8;
cloned[buf_idx + 2] = ((repl >> 40) & 0xff) as u8;
cloned[buf_idx + 1] = ((repl >> 48) & 0xff) as u8;
cloned[buf_idx] = ((repl >> 56) & 0xff) as u8;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like something that should get replaced by cloned[buf_idx..(buf_idx+8)].copy_from_slice(&repl.to_bytes_le()[..8]).

Comment on lines 1094 to 1097
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here.

Comment on lines 1079 to 1080
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once more for luck.

When the RQ mutator sees that a 8 byte comparison operand is equal to 8
bytes from the input, then it treats that as an I2S correspondence and
pushes a mutation that replaces those input bytes with a big-endian
encoding of the second comparison operand, `repl` (this process is done
on both the original/byte-swapped versions of the relevant values, to
handle either byte order)

This commit fixes a bit shifting bug (probably typo) that broke the
replacement and made RQ unable to solve the comparison. Instead, we'll
use copy_from_slice
AflppCmpLogFnOperands has fixed length (`[u8; 32]`) storage for `v0` and
`v1`, and separate `v0_len`/`v1_len` fields. However, the rust
constructor/setters only allow for `v0`/`v1` to be initialized from
slices that are _exactly_ 32 bytes long, since `copy_from_slice` panics
otherwise. So `v0_len`/`v1_len` can only be 32. The instrumentation in
`libafl_targets`' `cmplog.{h,c}` can create log entries where the
`v0_len`/`v1_len` values are anywhere between 0 and 32, so this change
allows us to do the same from rust.

Note: in LibAFL's cmplog instrumentation, `v0_len`/`v1_len` are
always _the same_ value. In AFLplusplus' implementation,
`__cmplog_rtn_hook_str` can create log entries with `v0_len !=
v1_len`.
@domenukk domenukk requested a review from addisoncrump April 12, 2026 18:23
Copy link
Copy Markdown
Member

@addisoncrump addisoncrump left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Do we have a good testbench for this?

@drewbarbs
Copy link
Copy Markdown
Author

Yeah I'd been using a tiny C target with an abort after comparisons to 16, 32, and 64 bit magic numbers. I can add it as a baby fuzzer and add it to the CI test list?

@addisoncrump
Copy link
Copy Markdown
Member

Please do, thanks :)

@addisoncrump addisoncrump self-requested a review April 13, 2026 14:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants