Coverage Report

Created: 2026-11-12 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libcoap/tests/oss-fuzz/cache_key_target.c
Line
Count
Source
1
/* Cache-key derivation fuzzer for libcoap.
2
 *
3
 * src/coap_cache.c is at ~21% line coverage. The cache layer derives a
4
 * stable key from a CoAP PDU by hashing a canonicalised view of the
5
 * options (with per-context "ignore" lists and the Block options handled
6
 * specially). The exposed entrypoints — coap_cache_derive_key /
7
 * coap_cache_derive_key_w_ignore / coap_cache_get_by_pdu — all consume an
8
 * attacker-influenced PDU.
9
 *
10
 * Strategy: parse a fuzz-supplied byte string as a CoAP UDP PDU, then
11
 * call every cache-key derivation entrypoint against it, varying the
12
 * "ignore options" set. coap_new_cache_entry is also driven on the
13
 * happy path so the cache-entry hash insertion (utlist macros over
14
 * attacker-controlled key bytes) is exercised.
15
 */
16
17
#include "coap3/coap_internal.h"
18
#include "coap3/coap_cache.h"
19
20
#include <stdint.h>
21
#include <stdlib.h>
22
#include <string.h>
23
24
int
25
60
LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
26
60
  if (size < 5 || size > 4096)
27
24
    return 0;
28
29
36
  coap_context_t *ctx = NULL;
30
36
  coap_session_t *session = NULL;
31
36
  coap_pdu_t *pdu = NULL;
32
36
  coap_address_t addr;
33
34
36
  coap_startup();
35
36
  coap_set_log_level(COAP_LOG_EMERG);
36
37
36
  ctx = coap_new_context(NULL);
38
36
  if (!ctx)
39
0
    goto cleanup;
40
41
36
  coap_address_init(&addr);
42
36
  addr.addr.sa.sa_family = AF_INET;
43
36
  session = coap_new_client_session(ctx, NULL, &addr, COAP_PROTO_UDP);
44
36
  if (!session)
45
0
    goto cleanup;
46
36
  session->state = COAP_SESSION_STATE_ESTABLISHED;
47
48
  /* First byte selects ignore-set + session-based flag. Remaining bytes
49
   * are parsed as a UDP PDU. */
50
36
  uint8_t selector = data[0];
51
36
  const uint8_t *pdu_bytes = data + 1;
52
36
  size_t pdu_len = size - 1;
53
54
36
  pdu = coap_pdu_init(COAP_MESSAGE_CON, COAP_REQUEST_CODE_GET, 0,
55
36
                      coap_session_max_pdu_size(session));
56
36
  if (!pdu)
57
0
    goto cleanup;
58
36
  if (!coap_pdu_parse(COAP_PROTO_UDP, pdu_bytes, pdu_len, pdu))
59
34
    goto cleanup;
60
61
2
  coap_cache_session_based_t sb = (selector & 0x01)
62
2
                                  ? COAP_CACHE_IS_SESSION_BASED
63
2
                                  : COAP_CACHE_NOT_SESSION_BASED;
64
65
  /* Plain derive_key. */
66
2
  {
67
2
    coap_cache_key_t *k = coap_cache_derive_key(session, pdu, sb);
68
2
    coap_delete_cache_key(k);
69
2
  }
70
71
  /* derive_key_w_ignore with a small ignore list — exercises the
72
   * is_cache_key() option filter that walks the (option_num, ignore_count)
73
   * tuples. */
74
2
  {
75
2
    static const uint16_t ignore_set[] = {
76
2
      COAP_OPTION_ETAG, COAP_OPTION_OBSERVE, COAP_OPTION_BLOCK1,
77
2
      COAP_OPTION_BLOCK2, COAP_OPTION_URI_HOST,
78
2
    };
79
2
    size_t n = (selector >> 1) % (sizeof(ignore_set) / sizeof(ignore_set[0]) + 1);
80
2
    coap_cache_key_t *k = coap_cache_derive_key_w_ignore(session, pdu, sb,
81
2
                                                         ignore_set, n);
82
2
    coap_delete_cache_key(k);
83
2
  }
84
85
  /* Drive coap_cache_get_by_pdu (lookup) and coap_new_cache_entry
86
   * (insertion + hash chain walk). */
87
2
  {
88
2
    coap_cache_entry_t *entry =
89
2
        coap_new_cache_entry(session, pdu,
90
2
                             (selector & 0x10) ? COAP_CACHE_RECORD_PDU
91
2
                             : COAP_CACHE_NOT_RECORD_PDU,
92
2
                             sb, 0);
93
2
    (void)coap_cache_get_by_pdu(session, pdu, sb);
94
2
    if (entry)
95
2
      coap_delete_cache_entry(ctx, entry);
96
2
  }
97
98
36
cleanup:
99
36
  if (pdu)
100
36
    coap_delete_pdu(pdu);
101
36
  if (session)
102
36
    coap_session_release(session);
103
36
  if (ctx)
104
36
    coap_free_context(ctx);
105
36
  coap_cleanup();
106
36
  return 0;
107
2
}