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
2 changes: 1 addition & 1 deletion ccan/README
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
CCAN imported from https://github.com/rustyrussell/ccan.

CCAN version: fe99a8e0
CCAN version: 17db5e13
19 changes: 19 additions & 0 deletions ccan/ccan/array_size/test/compile_fail-void-ptr.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#include <ccan/array_size/array_size.h>

int main(void)
{
void *vp = (void *)0;
#ifdef FAIL
/* A void * is a pointer, not an array: the typeof/types_compatible_p
* guard must reject this (unlike comparison-based checks, which a
* void type silently bypasses -- see the check_type/container_of
* audits). */
return ARRAY_SIZE(vp);
#if !HAVE_TYPEOF || !HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if _array_size_chk is a noop."
#endif
#else
(void)vp;
return 0;
#endif
}
16 changes: 11 additions & 5 deletions ccan/ccan/asort/asort.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@
#include <string.h>
#include <stdbool.h>

/* Vendored glibc code uses GNU void * arithmetic. */
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wpointer-arith"

/* glibc-internal type, mapped to ccan's equivalent. */
typedef _total_order_cb __compar_d_fn_t;

/* glibc-internal helpers, not available outside glibc. */
static inline void *__mempcpy(void *dst, const void *src, size_t n)
static inline void *asort_mempcpy(void *dst, const void *src, size_t n)
{
return (char *) memcpy (dst, src, n) + n;
}
Expand All @@ -54,8 +58,8 @@ __memswap (void *__restrict p1, void *__restrict p2, size_t n)
while (n > SWAP_GENERIC_SIZE)
{
memcpy (tmp, p1, SWAP_GENERIC_SIZE);
p1 = __mempcpy (p1, p2, SWAP_GENERIC_SIZE);
p2 = __mempcpy (p2, tmp, SWAP_GENERIC_SIZE);
p1 = asort_mempcpy (p1, p2, SWAP_GENERIC_SIZE);
p2 = asort_mempcpy (p2, tmp, SWAP_GENERIC_SIZE);
n -= SWAP_GENERIC_SIZE;
}
while (n > 0)
Expand Down Expand Up @@ -316,13 +320,13 @@ msort_with_tmp (const struct msort_param *p, void *b, size_t n)
{
if (cmp (b1, b2, arg) <= 0)
{
tmp = (char *) __mempcpy (tmp, b1, s);
tmp = (char *) asort_mempcpy (tmp, b1, s);
b1 += s;
--n1;
}
else
{
tmp = (char *) __mempcpy (tmp, b2, s);
tmp = (char *) asort_mempcpy (tmp, b2, s);
b2 += s;
--n2;
}
Expand Down Expand Up @@ -452,4 +456,6 @@ _asort (void *const pbase, size_t total_elems, size_t size,
}
}

#pragma GCC diagnostic pop

#endif /* !HAVE_QSORT_R_PRIVATE_LAST */
6 changes: 6 additions & 0 deletions ccan/ccan/asort/asort.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ _asort((base), (num), sizeof(*(base)), \
total_order_cast((cmp), *(base), (ctx)), (ctx))

#if HAVE_QSORT_R_PRIVATE_LAST
/* qsort_r is only declared under _GNU_SOURCE, which must precede the
* first libc include — we can't control our includers, so declare it
* ourselves (the configurator only sets this where this GNU signature
* was detected). */
void qsort_r(void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *, void *), void *arg);
#define _asort(b, n, s, cmp, ctx) qsort_r(b, n, s, cmp, ctx)
#else
void _asort(void *base, size_t nmemb, size_t size,
Expand Down
94 changes: 94 additions & 0 deletions ccan/ccan/asort/test/run-fallback-build.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/* Regression test for the !HAVE_QSORT_R_PRIVATE_LAST fallback in asort.c.
*
* The vendored glibc mergesort fallback must compile on any supported
* platform, including glibc systems where config.h legitimately has
* HAVE_QSORT_R_PRIVATE_LAST == 0 (e.g. ccanlint's reduce_features
* check, hand-written or cross-compilation configs). glibc's string.h
* declares __mempcpy under _DEFAULT_SOURCE (on by default), so the
* fallback's "static inline void *__mempcpy(...)" helper collides with
* the libc declaration: "error: static declaration of '__mempcpy'
* follows non-static declaration". This test forces the fallback on
* and therefore fails to compile on glibc today; after the helper is
* renamed it must compile and pass everywhere.
*/
#include "config.h"
#undef HAVE_QSORT_R_PRIVATE_LAST
#define HAVE_QSORT_R_PRIVATE_LAST 0
#include <ccan/asort/asort.h>
#include <ccan/asort/asort.c>
#include <ccan/tap/tap.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static int cmp_int(const int *a, const int *b, int *count)
{
(*count)++;
return (*a > *b) - (*a < *b);
}

struct big { char pad[9]; int key; char pad2[31]; }; /* 44 bytes: indirect */
static int cmp_big(const struct big *a, const struct big *b, int *count)
{
(*count)++;
return (a->key > b->key) - (a->key < b->key);
}

static bool sorted_ints(const int *a, size_t n)
{
for (size_t i = 1; i < n; i++)
if (a[i-1] > a[i])
return false;
return true;
}

int main(void)
{
alarm(10);
plan_tests(3);

/* Small array: mergesort with the stack buffer. */
{
int a[50];
int count = 0;
for (size_t i = 0; i < 50; i++)
a[i] = (int)((i * 37 + 11) % 23);
asort(a, 50, cmp_int, &count);
ok1(sorted_ints(a, 50) && count > 0);
}

/* 40000 bytes: exceeds QSORT_STACK_SIZE, takes the malloc path. */
{
size_t n = 10000;
int *a = malloc(n * sizeof(*a));
int count = 0;
unsigned long long r = 12345;
for (size_t i = 0; i < n; i++) {
r ^= r << 13; r ^= r >> 7; r ^= r << 17;
a[i] = (int)r;
}
asort(a, n, cmp_int, &count);
ok1(sorted_ints(a, n));
free(a);
}

/* Elements larger than 32 bytes: indirect (pointer) mergesort. */
{
size_t n = 100;
struct big *b = malloc(n * sizeof(*b));
int count = 0;
bool ok = true;
for (size_t i = 0; i < n; i++) {
memset(&b[i], (int)(i & 0xff), sizeof(*b));
b[i].key = (int)((i * 53) % 31);
}
asort(b, n, cmp_big, &count);
for (size_t i = 1; i < n; i++)
if (b[i-1].key > b[i].key)
ok = false;
ok1(ok);
free(b);
}

return exit_status();
}
45 changes: 45 additions & 0 deletions ccan/ccan/asort/test/run-include-order.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* Regression test: asort.h must be usable from a translation unit that
* included libc headers before it.
*
* With HAVE_QSORT_R_PRIVATE_LAST == 1 the asort() macro expands to a
* direct call of qsort_r (asort.h:26), but glibc only declares qsort_r
* under _GNU_SOURCE, and config.h's "#define _GNU_SOURCE" comes too
* late once any libc header (via features.h) was already processed.
* The result is a call to an undeclared function: a hard error on
* clang >= 15 and gcc >= 14 (C99+ implicit function declarations), a
* warning plus UB-ish implicit decl on older gcc. Today this file
* fails to compile under clang; after routing qsort_r through a real
* _asort() function defined in asort.c (where config.h is included
* first) it must compile cleanly and pass.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <ccan/asort/asort.h>
#include <ccan/asort/asort.c>
#include <ccan/tap/tap.h>

static int cmp_int(const int *a, const int *b, void *ctx)
{
(void)ctx;
return (*a > *b) - (*a < *b);
}

int main(void)
{
int a[7] = { 7, 1, 6, 2, 5, 3, 4 };
bool ok = true;

alarm(10);
plan_tests(1);

asort(a, 7, cmp_int, NULL);
for (size_t i = 1; i < 7; i++)
if (a[i-1] > a[i])
ok = false;
ok1(ok);

return exit_status();
}
2 changes: 1 addition & 1 deletion ccan/ccan/bitmap/bitmap.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ typedef unsigned long bitmap_word;

#define BITMAP_WORD_BITS (sizeof(bitmap_word) * CHAR_BIT)
#define BITMAP_NWORDS(_n) \
(((_n) + BITMAP_WORD_BITS - 1) / BITMAP_WORD_BITS)
(((_n) / BITMAP_WORD_BITS) + (((_n) % BITMAP_WORD_BITS) != 0))

#define BITMAP_WORD_0 (0)
#define BITMAP_WORD_1 ((bitmap_word)-1UL)
Expand Down
48 changes: 48 additions & 0 deletions ccan/ccan/bitmap/test/run-sizeof-overflow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/* Regression test for the allocation-sizing overflow in BITMAP_NWORDS /
* bitmap_sizeof (ccan/bitmap/bitmap.h:15-16, 31-34).
*
* BITMAP_NWORDS(_n) computes ((_n) + BITMAP_WORD_BITS - 1) /
* BITMAP_WORD_BITS; the addition wraps for nbits within
* BITMAP_WORD_BITS-1 of ULONG_MAX, so bitmap_sizeof() returns a tiny
* size (0 for ULONG_MAX) and bitmap_alloc() then returns a non-NULL
* pointer to a 0-byte allocation that purports to hold ~2^64 bits; the
* first bitmap_set_bit() writes out of bounds (observed: ASan
* heap-buffer-overflow, access of size 8 on a 1-byte region).
*
* Correct behavior: the reference computation below (divide first,
* then round up) never overflows and its product with
* sizeof(bitmap_word) still fits in size_t on both LP64 and ILP32, so
* bitmap_sizeof() must equal it for every nbits, including ULONG_MAX
* (bitmap_alloc() will then correctly fail with NULL for the
* unmappable ~2^61-byte request).
*/
#include <ccan/bitmap/bitmap.h>
#include <ccan/bitmap/bitmap.c>
#include <ccan/tap/tap.h>
#include <limits.h>
#include <unistd.h>

static unsigned long ref_nwords(unsigned long nbits)
{
return nbits / BITMAP_WORD_BITS + ((nbits % BITMAP_WORD_BITS) != 0);
}

int main(void)
{
/* Largest nbits that does NOT wrap the + (BITS-1) addition. */
unsigned long edge = ULONG_MAX - (BITMAP_WORD_BITS - 1);

alarm(10);
plan_tests(4);

/* These two fail against the current code (both return 0). */
ok1(BITMAP_NWORDS(ULONG_MAX) == ref_nwords(ULONG_MAX));
ok1(bitmap_sizeof(ULONG_MAX) ==
ref_nwords(ULONG_MAX) * sizeof(bitmap_word));

/* Boundary sanity: these already pass today and must keep passing. */
ok1(BITMAP_NWORDS(edge) == ref_nwords(edge));
ok1(bitmap_sizeof(edge) == ref_nwords(edge) * sizeof(bitmap_word));

return exit_status();
}
2 changes: 1 addition & 1 deletion ccan/ccan/bitops/bitops.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ static inline int bitops_ffs32(uint32_t u)
/**
* bitops_ffs64: find lowest set bit in a uint64_t
*
* Returns 1 for least significant bit, 32 for most significant bit, 0
* Returns 1 for least significant bit, 64 for most significant bit, 0
* for no bits set.
*/
static inline int bitops_ffs64(uint64_t u)
Expand Down
1 change: 1 addition & 0 deletions ccan/ccan/breakpoint/_info
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ int main(int argc, char *argv[])

if (strcmp(argv[1], "depends") == 0) {
printf("ccan/compiler\n");
printf("ccan/mem\n");
return 0;
}

Expand Down
23 changes: 18 additions & 5 deletions ccan/ccan/breakpoint/breakpoint.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,26 +7,39 @@

bool breakpoint_initialized;
bool breakpoint_under_debug;
pid_t breakpoint_pid;

static volatile sig_atomic_t trapped;

/* This doesn't get called if we're under GDB. */
static void trap(int signum)
{
breakpoint_initialized = true;
trapped = true;
}

void breakpoint_init(void)
{
struct sigaction old, new;
sigset_t mask, oldmask;

new.sa_handler = trap;
new.sa_flags = 0;
sigemptyset(&new.sa_mask);
sigaction(SIGTRAP, &new, &old);

/* If SIGTRAP is blocked, the probe would pend (and kill us when
* the caller restores its mask), not run the handler. */
sigemptyset(&mask);
sigaddset(&mask, SIGTRAP);
sigprocmask(SIG_UNBLOCK, &mask, &oldmask);

trapped = false;
kill(getpid(), SIGTRAP);

sigprocmask(SIG_SETMASK, &oldmask, NULL);
sigaction(SIGTRAP, &old, NULL);

if (!breakpoint_initialized) {
breakpoint_initialized = true;
breakpoint_under_debug = true;
}
breakpoint_pid = getpid();
breakpoint_initialized = true;
breakpoint_under_debug = !trapped;
}
9 changes: 8 additions & 1 deletion ccan/ccan/breakpoint/breakpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,20 @@
void breakpoint_init(void) COLD;
extern bool breakpoint_initialized;
extern bool breakpoint_under_debug;
extern pid_t breakpoint_pid;

/**
* breakpoint - stop if running under the debugger.
*
* The first call detects the debugger via a SIGTRAP probe. This is
* not thread-safe: either call breakpoint_init() explicitly at
* program start (before creating threads), or don't let first use
* race.
*/
static inline void breakpoint(void)
{
if (!breakpoint_initialized)
/* Detection state doesn't carry across fork(). */
if (!breakpoint_initialized || breakpoint_pid != getpid())
breakpoint_init();
if (breakpoint_under_debug)
kill(getpid(), SIGTRAP);
Expand Down
Loading
Loading