Skip to content

[libafl-targets] Value profile guidance for strcmp/memcmp interceptor and friends #3042

@ammaraskar

Description

@ammaraskar

Currently in libfuzzer, the hooks for strncmp, __sanitizer_weak_hook_strncmp calls into:

fuzzer::TPC.AddValueForMemcmp(caller_pc, s1, s2, n, /*StopAtZero*/true);

and then AddValueForMemcmp in turn does these two calls:

  ValueProfileMap.AddValue(Idx);
  TORCW.Insert(Idx ^ Hash, Word(B1, Len), Word(B2, Len));

TORCW is the comparison logging mechanism but notice it also has ValueProfileMap guidance here.

(same code in AFL++ here)


In comparison, the hook for strncmp in libafl-targets does:

__libafl_targets_cmplog_routines_len(k, s1 as *const u8, s2 as *const u8, actual_len);

which in turn only adds the value to the cmplog map:

MEMCPY(libafl_cmplog_map_ptr->vals.routines[k][hits].v0, ptr1, len);
MEMCPY(libafl_cmplog_map_ptr->vals.routines[k][hits].v1, ptr2, len);


This prevents libafl-libfuzzer from being able to solve things like this little harness (full harness attached harness.cpp.txt):

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
    if (Size == 0) return 0;
    char* encoded = (char*) malloc(Size * 3);
    if (encoded == NULL) return 0;

    int b64_length = b64_encode((unsigned char*) Data, Size, (unsigned char*) encoded);
    // "Hello " in base64 so it's not visible to cmplog
    if (strncmp(encoded, "SGVsbG8g", 8) == 0) {
        // "World" in base64 so it's not visible to cmplog
        if (b64_length > 8 && strncmp(encoded + b64_length - 8, "V29ybGQ=", 8) == 0) {
            abort();
        }
    }

    free(encoded);
    return 0;
}

libfuzzer with value profiles can solve this rather easily because it breaks the strncmp into a brute-force of the base64 output:

$ time ./libfuzzer -use_value_profile=1
INFO: Running with entropic power schedule (0xFF, 100).
INFO: Seed: 3161432840
INFO: Loaded 1 modules   (16 inline 8-bit counters): 16 [0x55a96f6e0fe1, 0x55a96f6e0ff1),
INFO: Loaded 1 PC tables (16 PCs): 16 [0x55a96f6e0ff8,0x55a96f6e10f8),
INFO: -max_len is not provided; libFuzzer will not generate inputs larger than 4096 bytes
INFO: A corpus is not provided, starting from an empty corpus
#2      INITED cov: 7 ft: 24 corp: 1/1b exec/s: 0 rss: 26Mb
...
0x48,0x65,0x6c,0x6c,0x6f,0x20,0x57,0x6f,0x72,0x6c,0x64,
Hello World
artifact_prefix='./'; Test unit written to ./crash-0a4d55a8d778e5022fab701977c5d840bbc486d0
Base64: SGVsbG8gV29ybGQ=

real    0m20.850s
user    0m20.556s
sys     0m0.068s
...

but using libafl's libfuzzer shim for the same thing:

$ time ./libafl-libfuzzer -use_value_profile=1
WARNING: cowardly refusing to use grimoire since we cannot determine if the input is primarily text; set -grimoire=1 or provide a corpus directory.
[UserStats   #0]  (GLOBAL) run time: 0h-0m-0s, clients: 1, corpus: 0, objectives: 0, executions: 0, exec/sec: 0.000, edges: 43.750%
                  (CLIENT) corpus: 0, objectives: 0, executions: 0, exec/sec: 0.000, edges: 7/16 (43%)
[UserStats   #0]  (GLOBAL) run time: 0h-0m-0s, clients: 1, corpus: 0, objectives: 0, executions: 0, exec/sec: 0.000, edges: 43.750%, size_edges: 43.750%
...
[Client Heartbeat #0]  (GLOBAL) run time: 0h-4m-30s, clients: 1, corpus: 10, objectives: 0, executions: 2015729, exec/sec: 7.459k, cmps: 0.011%, edges: 68.750%, size_edges: 68.750%, stability: 100.000%
                       (CLIENT) corpus: 10, objectives: 0, executions: 2015729, exec/sec: 7.459k, cmps: 7/65536 (0%), edges: 11/16 (68%), size_edges: 11/16 (68%), stability: 11/11 (100%)
[Client Heartbeat #0]  (GLOBAL) run time: 0h-4m-45s, clients: 1, corpus: 10, objectives: 0, executions: 2126692, exec/sec: 7.455k, cmps: 0.011%, edges: 68.750%, size_edges: 68.750%, stability: 100.000%
                       (CLIENT) corpus: 10, objectives: 0, executions: 2126692, exec/sec: 7.455k, cmps: 7/65536 (0%), edges: 11/16 (68%), size_edges: 11/16 (68%), stability: 11/11 (100%)
[Client Heartbeat #0]  (GLOBAL) run time: 0h-5m-0s, clients: 1, corpus: 10, objectives: 0, executions: 2240750, exec/sec: 7.463k, cmps: 0.011%, edges: 68.750%, size_edges: 68.750%, stability: 100.000%
                       (CLIENT) corpus: 10, objectives: 0, executions: 2240750, exec/sec: 7.463k, cmps: 7/65536 (0%), edges: 11/16 (68%), size_edges: 11/16 (68%), stability: 11/11 (100%)
^C

real    5m2.676s
user    4m59.504s
sys     0m3.055s

Notice there isn't an increase in cmps.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions