Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions dev/cppcheck/cppcheck_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Minimal reproducer for a cppcheck false positive.
*
* cppcheck incorrectly reports a memory leak for the pattern below.
* The pointer <local_ptr> is a local copy of <buf->ptr>. When realloc()
* fails and returns NULL, <local_ptr> becomes NULL, but the original
* allocation is still referenced by <buf->ptr>. The failure branch then
* uses <buf->ptr> to properly free the original memory, so there is no
* real leak.
*
* Reference: https://www.mail-archive.com/haproxy@formilux.org/msg46968.html
* Origin: src/quic_rx.c, function qc_try_store_new_token()
*/

#include <stdlib.h>
#include <string.h>

struct buf {
char *ptr;
size_t len;
};

static inline char *buf_ptr(struct buf b) { return b.ptr; }
static inline size_t buf_len(struct buf b) { return b.len; }
static inline void buf_free(struct buf *b) { free(b->ptr); b->ptr = NULL; b->len = 0; }

/* Update the raw-byte buffer <b> with <data> of length <len>, reallocating
* when needed. The buffer stores raw bytes; no null terminator is appended.
*
* False positive: cppcheck may report a memleak at the realloc() line because
* it sees <local_ptr> (a local variable) overwritten with the NULL return value
* of realloc() and concludes the original allocation is lost. In reality the
* original pointer is preserved in <b->ptr> and properly freed in the else
* branch, so no leak can occur.
*/
static void update_buf(struct buf *b, const char *data, size_t len)
{
char *local_ptr;

local_ptr = buf_ptr(*b);
if (len > buf_len(*b)) {
local_ptr = realloc(local_ptr, len);
if (local_ptr)
b->ptr = local_ptr;
else {
memset(buf_ptr(*b), 0, buf_len(*b));
buf_free(b);
}
}

if (local_ptr) {
memcpy(local_ptr, data, len);
b->len = len;
}
}

int main(void)
{
struct buf b = { NULL, 0 };

update_buf(&b, "hello", 5);
update_buf(&b, "world!", 6);
buf_free(&b);
return 0;
}
12 changes: 12 additions & 0 deletions src/cpuset.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ int ha_cpuset_set(struct hap_cpuset *set, int cpu)
set->cpuset |= (0x1 << cpu);
return 0;
#endif

return 1;
}

int ha_cpuset_clr(struct hap_cpuset *set, int cpu)
Expand All @@ -43,6 +45,8 @@ int ha_cpuset_clr(struct hap_cpuset *set, int cpu)
set->cpuset &= ~(0x1 << cpu);
return 0;
#endif

return 1;
}

void ha_cpuset_and(struct hap_cpuset *dst, struct hap_cpuset *src)
Expand Down Expand Up @@ -97,6 +101,8 @@ int ha_cpuset_count(const struct hap_cpuset *set)
#elif defined(CPUSET_USE_ULONG)
return my_popcountl(set->cpuset);
#endif

return 0;
}

int ha_cpuset_ffs(const struct hap_cpuset *set)
Expand All @@ -121,6 +127,8 @@ int ha_cpuset_ffs(const struct hap_cpuset *set)

return my_ffsl(set->cpuset);
#endif

return 0;
}

void ha_cpuset_assign(struct hap_cpuset *dst, struct hap_cpuset *src)
Expand Down Expand Up @@ -149,6 +157,8 @@ int ha_cpuset_isequal(const struct hap_cpuset *dst, const struct hap_cpuset *src
#elif defined(CPUSET_USE_ULONG)
return dst->cpuset == src->cpuset;
#endif

return 0;
}

int ha_cpuset_size()
Expand All @@ -160,6 +170,8 @@ int ha_cpuset_size()
return LONGBITS;

#endif

return 0;
}

/* Parse cpu sets. Each CPU set is either a unique number between 0 and
Expand Down
Loading