CCAN: update to post-kimi audit.

Lots of changes, so I've summarized the individual upstream commits.

May alter (fix!) CLN behavior:

   * 6b4e62ec tal/path: path_simplify() ".." handling
   * 4ceeef2b io: don't busy-spin with exclusive conn + pending always plan
   * 64822afc io: listener fds O_NONBLOCK
   * b02c1147, 2f04573d, 42486358 pipecmd: _exit, pre-fork fd leak, EINTR
   * f7062e12, 6da9c78d fdpass: reject-path fd leak, errno sign
   * 339b359a breakpoint: blocked-SIGTRAP + fork staleness (--dev-debugger only)
   * 018a4977 take: keep labelarr in sync in taken() (debug builds would give wrong taken() error)
   * 28f7c3fe check_type: hard-fail (would cause build-time failure)

The rest are not patterns used by CLN, or only happen on allocation failure (which always causes immediate exit):

   * 4122d490 tal/str: count regex capture groups via re_nsub, not text scan.  No such complex regexes used.
   * 5b6fca2c tal: allow tal_steal() to rescue an object being destroyed; we don't do this.
   * tal: 9da96c88 (CCAN_LIST_DEBUG only), ca38d33a (notifier edge), 328d96ff (needs type-mismatched tal_expand), 39d1a0a0 (harmless thinko), df66b082
     (path_readlink unused), 80416994 (UB never dereferenced), 0e926004 (doc), 3217faca (zero st_size; no /proc reads in CLN), 5d86e1fe (doc)
   * c0c9797f str: strcount empty needle - both CLN call sites pass constant non-empty needles
   * htable: af8ff95e (init_sized unused), d3da4521 (32-bit), ba8c4ad2 (needs billions of entries)
   * opt: 6c3faecc (bare '-' argv), dae5f5f4 (attribute/doc; CLN passes real args)
   * timer: f7b21467 (36k years at default granularity), 1127eb30 (needs failing custom allocator - CLN aborts on OOM)
   * mem: 351072ac (memcchr unused), dde9474c (fallback memrchr - glibc/musl provide it), 228161d9 (test), f5b48d0f (doc)
   * json_out: f14769e3, e9893f9e, d0401012, c91ae47b - all OOM/encoding-error paths; CLN aborts on OOM
   * membuf: 4ee105f0 (UB, correct results anyway), 4b2d55b8 (2^62-element buffers)
   * rune: cc63dceb (rune_dup unused), 51257cd9 (CLN fieldnames are alnum), fb4dbfa6 (needs embedded-NUL field values)
   * strset/strmap iterator rework: b5dcf30e, 19b73d88, 51c4da92, c127ff33 - additive API + recursion fix; CLN's maps are far too small to hit deep
     trees
   * crypto: 1c377ffb (32-bit only), 344ba97c (needs 8160-byte HKDF requests), b4316b44 (default SHACHAIN_BITS=64 immune)
   * f2b445b3 str/base32: CLN's one call site passes an exact-size buffer
   * intmap 1a1bb440 (sintmap unused), bitmap ab052a14 (2^61 bits), likely 8cfa5f8c (likely_stats unused), take e30e011d (only take_cleanup() on exit), rbuf 748a0692 (rbuf_fill unused), ptr_valid x4 (unused), cdump x3 (cdump_extract unused)
   * asort: 0d54244f, a13c9b97, d878b036 - fallback/build-hygiene only; CLN on glibc takes the qsort_r path and already compiled
   * Doc-only: 2b988863, 3e034139, 8fee253f, e3155f93, 6e830d68, 172ae94c, cbbd603e, 6f0b1b03
   * Test-only: 46acc9b6, 9df9809e, 4c23c54c
   * 5b2773b2 build_assert: _Static_assert - compile-time only; all 138 CLN uses are constants (same 138-use build check applies as check_type)
   * 01fbf784 container_of: NULL+0 UB fix - behavior identical
   * 0421034f cppmagic sentinel, 43cfe853 ilog ilog64 - portability/performance only

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
This commit is contained in:
Rusty Russell 2026-08-10 09:47:38 +09:30
parent 5a56a976b4
commit 6fbfdf1add
68 changed files with 1128 additions and 221 deletions

View file

@ -1,3 +1,3 @@
CCAN imported from https://github.com/rustyrussell/ccan.
CCAN version: fe99a8e0
CCAN version: 08af1cab

View file

@ -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;
}
@ -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)
@ -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;
}
@ -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 */

View file

@ -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,

View file

@ -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)

View file

@ -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)

View file

@ -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;
}

View file

@ -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);

View file

@ -1,13 +1,19 @@
/* CC0 (Public domain) - see LICENSE file for details */
#ifndef CCAN_BUILD_ASSERT_H
#define CCAN_BUILD_ASSERT_H
#include "config.h"
/**
* BUILD_ASSERT - assert a build-time dependency.
* @cond: the compile-time condition which must be true.
*
* Your compile will fail if the condition isn't true, or can't be evaluated
* by the compiler. This can only be used within a function.
* Your compile will fail if the condition isn't true. When the
* compiler supports C11 _Static_assert it will also fail if the
* condition can't be evaluated by the compiler; otherwise (older
* compilers) a non-constant condition is silently accepted (and is
* undefined behavior if false at runtime).
*
* This can only be used within a function.
*
* Example:
* #include <stddef.h>
@ -19,22 +25,38 @@
* return (char *)foo;
* }
*/
#if HAVE_STATIC_ASSERT
/* _Static_assert is a declaration, so do-while wrap avoids breaking if (x) BUILD_ASSERT... */
#define BUILD_ASSERT(cond) \
do { _Static_assert(cond, "BUILD_ASSERT"); } while(0)
#else
#define BUILD_ASSERT(cond) \
do { (void) sizeof(char [1 - 2*!(cond)]); } while(0)
#endif
/**
* BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.
* @cond: the compile-time condition which must be true.
*
* Your compile will fail if the condition isn't true, or can't be evaluated
* by the compiler. This can be used in an expression: its value is "0".
* Your compile will fail if the condition isn't true. When the
* compiler supports C11 _Static_assert it will also fail if the
* condition can't be evaluated by the compiler; otherwise (older
* compilers) a non-constant condition is silently accepted (and is
* undefined behavior if false at runtime).
*
* This can be used in an expression: its value is "0".
*
* Example:
* #define foo_to_char(foo) \
* ((char *)(foo) \
* + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
*/
#if HAVE_STATIC_ASSERT
#define BUILD_ASSERT_OR_ZERO(cond) \
(sizeof(struct { _Static_assert(cond, "BUILD_ASSERT_OR_ZERO"); char c; }) - 1)
#else
#define BUILD_ASSERT_OR_ZERO(cond) \
(sizeof(char [1 - 2*!(cond)]) - 1)
#endif
#endif /* CCAN_BUILD_ASSERT_H */

View file

@ -48,9 +48,10 @@ static struct token *tokenize(const void *ctx, const char *code)
} else if (code[i] == '/' && code[i+1] == '*') {
/* Multi-line comment. */
const char *end = strstr(code+i+2, "*/");
len = (end + 2) - (code + i);
if (!end)
len = strlen(code + i);
else
len = (end + 2) - (code + i);
if (tok_start != -1U) {
add_token(&toks, code+tok_start, i - tok_start);
tok_start = -1U;
@ -82,6 +83,10 @@ static struct token *tokenize(const void *ctx, const char *code)
start_of_line = false;
}
/* A trailing identifier running to EOF is still a token. */
if (tok_start != -1U)
add_token(&toks, code+tok_start, i - tok_start);
/* Add terminating NULL. */
tal_resizez(&toks, tal_count(toks) + 1);
return toks;
@ -92,6 +97,7 @@ struct parse_state {
const struct token *toks;
struct cdump_definitions *defs;
char *complaints;
unsigned int depth;
};
static const struct token *tok_peek(const struct token **toks)
@ -277,17 +283,28 @@ static void tok_take_unknown_statement(struct parse_state *ps)
static bool tok_take_expr(struct parse_state *ps, const char *term)
{
/* Recursion is one frame per nested ( or [: bound it. */
if (ps->depth++ == 100) {
complain(ps, "Expression nested too deeply");
goto fail;
}
while (!tok_is(&ps->toks, term)) {
if (tok_take_if(&ps->toks, "(")) {
if (!tok_take_expr(ps, ")"))
return false;
goto fail;
} else if (tok_take_if(&ps->toks, "[")) {
if (!tok_take_expr(ps, "]"))
return false;
goto fail;
} else if (!tok_take(&ps->toks))
return false;
goto fail;
}
return tok_take(&ps->toks);
if (!tok_take(&ps->toks))
goto fail;
ps->depth--;
return true;
fail:
ps->depth--;
return false;
}
static char *tok_take_expr_str(const tal_t *ctx,
@ -347,7 +364,12 @@ static bool tok_take_type(struct parse_state *ps, struct cdump_type **type)
/* Did we get some? */
if (ps->toks != types) {
name = string_of_toks(NULL, types, tok_peek(&ps->toks));
const struct token *until = tok_peek(&ps->toks);
if (!until) {
complain(ps, "EOF after type");
return false;
}
name = string_of_toks(NULL, types, until);
kind = CDUMP_UNKNOWN;
} else {
/* Try normal types (or simple typedefs, etc). */
@ -654,6 +676,7 @@ struct cdump_definitions *cdump_extract(const tal_t *ctx, const char *code,
ps.defs = tal(ctx, struct cdump_definitions);
ps.complaints = tal_strdup(ctx, "");
ps.code = code;
ps.depth = 0;
strmap_init(&ps.defs->enums);
strmap_init(&ps.defs->structs);

View file

@ -45,7 +45,15 @@
* ((encl_type *) \
* ((char *)(mbr_ptr) - offsetof(encl_type, mbr))))
*/
#if HAVE_TYPEOF
#if HAVE_TYPEOF && HAVE_BUILTIN_TYPES_COMPATIBLE_P
#include <ccan/build_assert/build_assert.h>
#define check_type(expr, type) \
BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(typeof(expr), type))
#define check_types_match(expr1, expr2) \
BUILD_ASSERT_OR_ZERO(__builtin_types_compatible_p(typeof(expr1), \
typeof(expr2)))
#elif HAVE_TYPEOF
#define check_type(expr, type) \
((typeof(expr) *)0 != (type *)0)

View file

@ -75,6 +75,7 @@
#else
#define CONST_FUNCTION
#endif
#endif
#ifndef PURE_FUNCTION
#if HAVE_ATTRIBUTE_PURE
@ -89,7 +90,6 @@
#define PURE_FUNCTION
#endif
#endif
#endif
#if HAVE_ATTRIBUTE_UNUSED
#ifndef UNNEEDED
@ -199,7 +199,7 @@
* // Use inline if compiler knows answer. Otherwise call function
* // to avoid copies of the same code everywhere.
* #define greek_name(g) \
* (IS_COMPILE_CONSTANT(greek) ? _greek_name(g) : greek_name(g))
* (IS_COMPILE_CONSTANT(g) ? _greek_name(g) : greek_name(g))
*/
#define IS_COMPILE_CONSTANT(expr) __builtin_constant_p(expr)
#else
@ -230,6 +230,7 @@
#endif
#ifndef WARN_DEPRECATED
#if HAVE_ATTRIBUTE_DEPRECATED
/**
* WARN_DEPRECATED - warn that a function/type/variable is deprecated when used.
@ -243,8 +244,9 @@
#else
#define WARN_DEPRECATED
#endif
#endif
#ifndef NO_NULL_ARGS
#if HAVE_ATTRIBUTE_NONNULL
/**
* NO_NULL_ARGS - specify that no arguments to this function can be NULL.
@ -255,7 +257,13 @@
* NO_NULL_ARGS char *my_copy(char *buf);
*/
#define NO_NULL_ARGS __attribute__((__nonnull__))
#else
#define NO_NULL_ARGS
#endif
#endif
#ifndef NON_NULL_ARGS
#if HAVE_ATTRIBUTE_NONNULL
/**
* NON_NULL_ARGS - specify that some arguments to this function can't be NULL.
* @...: 1-based argument numbers for which args can't be NULL.
@ -267,10 +275,11 @@
*/
#define NON_NULL_ARGS(...) __attribute__((__nonnull__(__VA_ARGS__)))
#else
#define NO_NULL_ARGS
#define NON_NULL_ARGS(...)
#endif
#endif
#ifndef RETURNS_NONNULL
#if HAVE_ATTRIBUTE_RETURNS_NONNULL
/**
* RETURNS_NONNULL - specify that this function cannot return NULL.
@ -284,7 +293,9 @@
#else
#define RETURNS_NONNULL
#endif
#endif
#ifndef LAST_ARG_NULL
#if HAVE_ATTRIBUTE_SENTINEL
/**
* LAST_ARG_NULL - specify the last argument of a variadic function must be NULL.
@ -298,7 +309,9 @@
#else
#define LAST_ARG_NULL
#endif
#endif
#ifndef cpu_supports
#if HAVE_BUILTIN_CPU_SUPPORTS
/**
* cpu_supports - test if current CPU supports the named feature.
@ -313,5 +326,6 @@
#else
#define cpu_supports(x) 0
#endif /* HAVE_BUILTIN_CPU_SUPPORTS */
#endif
#endif /* CCAN_COMPILER_H */

View file

@ -68,9 +68,10 @@ static inline char *container_of_or_null_(void *member_ptr, size_t offset)
}
#define container_of_or_null(member_ptr, containing_type, member) \
((containing_type *) \
container_of_or_null_(member_ptr, \
container_off(containing_type, member)) \
+ check_types_match(*(member_ptr), ((containing_type *)0)->member))
((void)check_types_match(*(member_ptr), \
((containing_type *)0)->member), \
container_of_or_null_(member_ptr, \
container_off(containing_type, member))))
/**
* container_off - get offset to enclosing structure

View file

@ -46,7 +46,7 @@
* expands to '1' if @a is '0', otherwise expands to '0'.
*/
#define _CPPMAGIC_ISPROBE(...) CPPMAGIC_2ND(__VA_ARGS__, 0)
#define _CPPMAGIC_PROBE() $, 1
#define _CPPMAGIC_PROBE() _cppmagic_probe, 1
#define _CPPMAGIC_ISZERO_0 _CPPMAGIC_PROBE()
#define CPPMAGIC_ISZERO(a_) \
_CPPMAGIC_ISPROBE(CPPMAGIC_GLUE2(_CPPMAGIC_ISZERO_, a_))
@ -139,6 +139,11 @@
*
* CPPMAGIC_MAP(@m, @a1, @a2, ... @an)
* expands to the expansion of @m(@a1) , @m(@a2) , ... , @m(@an)
*
* Note: an argument which expands to no tokens is not supported: it
* silently truncates the argument list at that point (before C23
* __VA_OPT__ there is no way to distinguish "expands to nothing"
* from "absent").
*/
#define _CPPMAGIC_MAP_() _CPPMAGIC_MAP
#define _CPPMAGIC_MAP(m_, a_, ...) \
@ -158,6 +163,9 @@
* CPPMAGIC_2MAP(@m, @a1, @b1, @a2, @b2, ..., @an, @bn)
* expands to the expansion of
* @m(@a1, @b1) , @m(@a2, @b2) , ... , @m(@an, @bn)
*
* Note: an argument which expands to no tokens is not supported
* (see CPPMAGIC_MAP).
*/
#define _CPPMAGIC_2MAP_() _CPPMAGIC_2MAP
#define _CPPMAGIC_2MAP(m_, a_, b_, ...) \
@ -176,6 +184,9 @@
*
* CPPMAGIC_JOIN(@d, @a1, @a2, ..., @an)
* expands to the expansion of @a1 @d @a2 @d ... @d @an
*
* Note: an argument which expands to no tokens is not supported
* (see CPPMAGIC_MAP).
*/
#define _CPPMAGIC_JOIN_() _CPPMAGIC_JOIN
#define _CPPMAGIC_JOIN(d_, a_, ...) \

View file

@ -13,7 +13,10 @@ void hkdf_sha256(void *okm, size_t okm_size,
struct hmac_sha256_ctx ctx;
unsigned char c;
assert(okm_size < 255 * sizeof(t));
assert(okm_size <= 255 * sizeof(t));
if (okm_size == 0)
return;
/* RFC 5869:
*

View file

@ -7,7 +7,7 @@
/**
* hkdf_sha256 - generate a derived key
* @okm: where to output the key
* @okm_size: the number of bytes pointed to by @okm (must be less than 255*32)
* @okm_size: the number of bytes pointed to by @okm (must be at most 255*32)
* @s: salt
* @ssize: the number of bytes pointed to by @s
* @k: pointer to input key

View file

@ -49,7 +49,8 @@ struct sha256_ctx {
uint32_t u32[16];
unsigned char u8[64];
} buf;
size_t bytes;
/* uint64_t: hashing 4GB+ must not wrap the length on 32-bit. */
uint64_t bytes;
#endif
};

View file

@ -86,8 +86,17 @@ bool shachain_add_hash(struct shachain *chain,
/* You have to insert them in order! */
assert(index == shachain_next_index(chain));
/* Reject out-of-domain indices (SHACHAIN_BITS < 64 builds, and
* the post-exhaustion wrap to UINT64_MAX). */
if (index > (UINT64_MAX >> (64 - SHACHAIN_BITS)))
return false;
pos = count_trailing_zeroes(index);
/* Beyond the chain domain (past exhaustion): no such slot. */
if (pos > SHACHAIN_BITS)
return false;
/* All derivable answers must be valid. */
/* FIXME: Is it sufficient to check just the next answer? */
for (i = 0; i < pos; i++) {

View file

@ -30,7 +30,6 @@ int main(int argc, char *argv[])
/* Test err() in child */
if (pipe(pfd))
abort();
fflush(stdout);
if (fork()) {
char buffer[BUFFER_MAX+1];
unsigned int i;
@ -63,7 +62,6 @@ int main(int argc, char *argv[])
/* Test errx() in child */
if (pipe(pfd))
abort();
fflush(stdout);
if (fork()) {
char buffer[BUFFER_MAX+1];
unsigned int i;
@ -94,7 +92,6 @@ int main(int argc, char *argv[])
/* Test warn() in child */
if (pipe(pfd))
abort();
fflush(stdout);
if (fork()) {
char buffer[BUFFER_MAX+1];
unsigned int i;
@ -127,7 +124,6 @@ int main(int argc, char *argv[])
/* Test warnx() in child */
if (pipe(pfd))
abort();
fflush(stdout);
if (fork()) {
char buffer[BUFFER_MAX+1];
unsigned int i;

View file

@ -4,6 +4,7 @@
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
bool fdpass_send(int sockout, int fd)
{
@ -71,11 +72,24 @@ int fdpass_recv(int sockin)
return -1;
cmsg = CMSG_FIRSTHDR(&msg);
if (!cmsg
|| cmsg->cmsg_len != CMSG_LEN(sizeof(fd))
if (!cmsg
|| cmsg->cmsg_level != SOL_SOCKET
|| cmsg->cmsg_type != SCM_RIGHTS) {
errno = -EINVAL;
errno = EINVAL;
return -1;
}
if (cmsg->cmsg_len != CMSG_LEN(sizeof(fd))) {
/* The kernel already installed any fds the message
* carried; don't leak them. */
if (cmsg->cmsg_len >= CMSG_LEN(0)
&& cmsg->cmsg_len <= msg.msg_controllen) {
int *fds = (int *)CMSG_DATA(cmsg);
size_t i, nfds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
for (i = 0; i < nfds; i++)
close(fds[i]);
}
errno = EINVAL;
return -1;
}

View file

@ -86,6 +86,12 @@ void htable_init(struct htable *ht,
ht->table = &ht->common_bits;
}
/* Number of buckets in the table. */
static inline size_t ht_size(const struct htable *ht)
{
return (size_t)1 << ht->bits;
}
/* Fill to 87.5% */
static inline size_t ht_max(const struct htable *ht)
{
@ -95,7 +101,7 @@ static inline size_t ht_max(const struct htable *ht)
/* Clean deleted if we're full, and more than 12.5% deleted */
static inline size_t ht_max_deleted(const struct htable *ht)
{
return ((size_t)1 << ht->bits) / 8;
return ht_size(ht) / 8;
}
bool htable_init_sized(struct htable *ht,
@ -108,11 +114,15 @@ bool htable_init_sized(struct htable *ht,
for (ht->bits = 1; ht_max(ht) < expect; ht->bits++) {
if (ht->bits == 30)
break;
/* Stop before the allocation size wraps (eg. 32-bit). */
if ((sizeof(size_t) << (ht->bits + 1)) == 0)
break;
}
ht->table = htable_alloc(ht, sizeof(size_t) << ht->bits);
if (!ht->table) {
ht->table = &ht->common_bits;
ht->bits = 0;
return false;
}
(void)htable_debug(ht, HTABLE_LOC);
@ -153,7 +163,7 @@ void htable_unlock(struct htable *ht)
static size_t hash_bucket(const struct htable *ht, size_t h)
{
return h & ((1 << ht->bits)-1);
return h & (ht_size(ht)-1);
}
static void *htable_val(const struct htable *ht,
@ -166,7 +176,7 @@ static void *htable_val(const struct htable *ht,
if (get_extra_ptr_bits(ht, ht->table[i->off]) == h2)
return get_raw_ptr(ht, ht->table[i->off]);
}
i->off = (i->off + 1) & ((1 << ht->bits)-1);
i->off = (i->off + 1) & (ht_size(ht)-1);
h2 &= ~perfect;
}
return NULL;
@ -182,13 +192,13 @@ void *htable_firstval_(const struct htable *ht,
void *htable_nextval_(const struct htable *ht,
struct htable_iter *i, size_t hash)
{
i->off = (i->off + 1) & ((1 << ht->bits)-1);
i->off = (i->off + 1) & (ht_size(ht)-1);
return htable_val(ht, i, hash, 0);
}
void *htable_first_(const struct htable *ht, struct htable_iter *i)
{
for (i->off = 0; i->off < (size_t)1 << ht->bits; i->off++) {
for (i->off = 0; i->off < ht_size(ht); i->off++) {
if (entry_is_valid(ht->table[i->off]))
return get_raw_ptr(ht, ht->table[i->off]);
}
@ -197,7 +207,7 @@ void *htable_first_(const struct htable *ht, struct htable_iter *i)
void *htable_next_(const struct htable *ht, struct htable_iter *i)
{
for (i->off++; i->off < (size_t)1 << ht->bits; i->off++) {
for (i->off++; i->off < ht_size(ht); i->off++) {
if (entry_is_valid(ht->table[i->off]))
return get_raw_ptr(ht, ht->table[i->off]);
}
@ -243,7 +253,7 @@ static COLD void fixup_table_common(struct htable *ht, uintptr_t maskdiff)
again:
bitsdiff = ht->common_bits & maskdiff;
for (i = 0; i < (size_t)1 << ht->bits; i++) {
for (i = 0; i < ht_size(ht); i++) {
uintptr_t e;
if (!entry_is_valid(e = ht->table[i]))
continue;
@ -296,7 +306,7 @@ static void ht_add(struct htable *ht, const void *new, size_t h)
while (entry_is_valid(ht->table[i])) {
perfect = 0;
i = (i + 1) & ((1 << ht->bits)-1);
i = (i + 1) & (ht_size(ht)-1);
}
ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect);
if (!entry_is_valid(ht->table[i]))
@ -306,11 +316,16 @@ static void ht_add(struct htable *ht, const void *new, size_t h)
static COLD bool double_table(struct htable *ht)
{
unsigned int i;
size_t oldnum = (size_t)1 << ht->bits;
size_t oldnum = ht_size(ht);
size_t newsize = sizeof(size_t) << (ht->bits+1);
uintptr_t *oldtable, e;
/* 32-bit: doubling can wrap the allocation size to 0. */
if (newsize == 0)
return false;
oldtable = ht->table;
ht->table = htable_alloc(ht, sizeof(size_t) << (ht->bits+1));
ht->table = htable_alloc(ht, newsize);
if (!ht->table) {
ht->table = oldtable;
return false;
@ -350,8 +365,8 @@ static COLD void rehash_table(struct htable *ht)
/* Beware wrap cases: we need to start from first empty bucket. */
for (start = 0; ht->table[start]; start++);
for (i = 0; i < (size_t)1 << ht->bits; i++) {
size_t h = (i + start) & ((1 << ht->bits)-1);
for (i = 0; i < ht_size(ht); i++) {
size_t h = (i + start) & (ht_size(ht)-1);
e = ht->table[h];
if (!e)
continue;
@ -427,7 +442,7 @@ bool htable_del_(struct htable *ht, size_t h, const void *p)
void htable_delval_(struct htable *ht, struct htable_iter *i)
{
assert(i->off < (size_t)1 << ht->bits);
assert(i->off < ht_size(ht));
assert(entry_is_valid(ht->table[i->off]));
ht->elems--;
@ -446,7 +461,7 @@ void *htable_pick_(const struct htable *ht, size_t seed, struct htable_iter *i)
if (!i)
i = &unwanted;
i->off = seed % ((size_t)1 << ht->bits);
i->off = seed % ht_size(ht);
e = htable_next(ht, i);
if (!e)
e = htable_first(ht, i);

View file

@ -131,7 +131,7 @@ int ilog64_nz(uint64_t _v) CONST_FUNCTION;
#endif /* builtin_ilog32_nz */
#ifdef builtin_ilog64_nz
#define ilog32(_v) ((_v) ? builtin_ilog32_nz(_v) : 0)
#define ilog64(_v) ((_v) ? builtin_ilog64_nz(_v) : 0)
#define ilog64_nz(_v) builtin_ilog64_nz(_v)
#else
#define ilog64_nz(_v) ilog64(_v)

View file

@ -467,7 +467,8 @@ static inline void *sintmap_first_(const struct intmap *map,
{
intmap_index_t i;
void *ret = intmap_first_(map, &i);
*indexp = SINTMAP_UNOFF(i);
if (ret)
*indexp = SINTMAP_UNOFF(i);
return ret;
}
@ -495,7 +496,8 @@ static inline void *sintmap_last_(const struct intmap *map,
{
intmap_index_t i;
void *ret = intmap_last_(map, &i);
*indexp = SINTMAP_UNOFF(i);
if (ret)
*indexp = SINTMAP_UNOFF(i);
return ret;
}

View file

@ -33,6 +33,11 @@ struct io_listener *io_new_listener_(const tal_t *ctx, int fd,
l->ctx = ctx;
if (!add_listener(l))
return tal_free(l);
/* Keep accept() async: a connection which vanishes between
* poll() and accept() (eg. peer RST) must not block the loop. */
io_fd_block(fd, false);
return l;
}

View file

@ -310,6 +310,18 @@ static bool handle_always(void)
return false;
}
/* Is there an always plan we can actually run right now? */
static bool always_runnable(void)
{
size_t i;
for (i = 0; i < num_always; i++) {
if (!num_exclusive || *exclusive(always[i]))
return true;
}
return false;
}
bool backend_set_exclusive(struct io_plan *plan, bool excl)
{
bool *excl_ptr = exclusive(plan);
@ -374,7 +386,7 @@ void *io_loop(struct timers *timers, struct timer **expired)
{
void *ret;
/* This ensures we don't always service lower fds first */
static int fairness_counter;
static size_t fairness_counter;
/* if timers is NULL, expired must be. If not, not. */
assert(!timers == !expired);
@ -415,7 +427,7 @@ void *io_loop(struct timers *timers, struct timer **expired)
}
/* Don't wait if we have always requests pending! */
if (num_always != 0)
if (always_runnable())
ms_timeout = 0;
/* We do this temporarily, assuming exclusive is unusual */

View file

@ -61,6 +61,8 @@ static struct json_escape *escape(const tal_t *ctx,
/* Worst case: all \uXXXX */
esc = (struct json_escape *)tal_arr(ctx, char, len * 6 + 1);
if (!esc)
return NULL;
for (i = n = 0; i < len; i++, n++) {
char escape = 0;

View file

@ -115,7 +115,8 @@ static void unindent(struct json_out *jout, char type)
jout->empty = false;
}
/* Make sure jout->outbuf has room for len: return pointer */
/* Make sure jout->outbuf has room for len: return pointer, or NULL
* if the allocation failed. */
static char *mkroom(struct json_out *jout, size_t len)
{
ptrdiff_t delta = membuf_prepare_space(&jout->outbuf, len);
@ -123,6 +124,11 @@ static char *mkroom(struct json_out *jout, size_t len)
if (delta && jout->move_cb)
jout->move_cb(jout, delta, jout->cb_arg);
/* membuf_prepare_space() documents checking membuf_num_space()
* to detect allocation failure. */
if (membuf_num_space(&jout->outbuf) < len)
return NULL;
return membuf_space(&jout->outbuf);
}
@ -218,6 +224,7 @@ bool json_out_addv(struct json_out *jout,
size_t fmtlen, avail;
va_list ap2;
char *dst;
int vsnprintf_ret;
if (!json_out_member_direct(jout, fieldname, 0))
return false;
@ -236,7 +243,12 @@ bool json_out_addv(struct json_out *jout,
/* Try printing in place first. */
dst = membuf_space(&jout->outbuf);
fmtlen = vsnprintf(dst + quote, avail, fmt, ap);
vsnprintf_ret = vsnprintf(dst + quote, avail, fmt, ap);
if (vsnprintf_ret < 0) {
dst = NULL;
goto out;
}
fmtlen = vsnprintf_ret;
/* Horrible subtlety: vsnprintf *will* NUL terminate, even if it means
* chopping off the last character. So if fmtlen ==
@ -259,6 +271,10 @@ bool json_out_addv(struct json_out *jout,
if (json_escape_needed(dst + quote, fmtlen)) {
struct json_escape *e;
e = json_escape_len(NULL, dst + quote, fmtlen);
if (!e) {
dst = NULL;
goto out;
}
fmtlen = strlen(e->s);
dst = mkroom(jout, fmtlen + (int)quote*2);
if (!dst)
@ -307,6 +323,8 @@ bool json_out_addstrn(struct json_out *jout,
if (json_escape_needed(str, len)) {
e = json_escape_len(NULL, str, len);
if (!e)
return false;
str = e->s;
len = strlen(str);
} else
@ -328,11 +346,15 @@ bool json_out_add_splice(struct json_out *jout,
{
const char *p;
size_t len;
char *dst;
p = json_out_contents(src, &len);
if (!p)
return false;
memcpy(json_out_member_direct(jout, fieldname, len), p, len);
dst = json_out_member_direct(jout, fieldname, len);
if (!dst)
return false;
memcpy(dst, p, len);
return true;
}

View file

@ -99,7 +99,7 @@ char *likely_stats(unsigned int min_hits, unsigned int percent)
}
}
if (worst_ratio * 100 > percent)
if (!worst || worst_ratio * 100 > percent)
return NULL;
maxlen = strlen(worst->condstr) +

View file

@ -33,7 +33,7 @@ void *memrchr(const void *s, int c, size_t n)
unsigned char *p = (unsigned char *)s;
while (n) {
if (p[n-1] == c)
if (p[n-1] == (unsigned char)c)
return p + n - 1;
n--;
}
@ -56,11 +56,11 @@ void *mempbrkm(const void *data_, size_t len, const void *accept_, size_t accept
void *memcchr(void const *data, int c, size_t data_len)
{
char const *p = data;
unsigned char const *p = data;
size_t i;
for (i = 0; i < data_len; i++)
if (p[i] != c)
if (p[i] != (unsigned char)c)
return (void *)&p[i];
return NULL;

View file

@ -219,6 +219,9 @@ static inline bool memends_str(const void *a, size_t al, const char *s)
* @al: length of first memory range
* @b: pointer to second memory range
* @al: length of second memory range
*
* Note that a zero-length range counts as overlapping any range that
* straddles its address.
*/
CONST_FUNCTION
static inline bool memoverlaps(const void *a_, size_t al,

View file

@ -1,6 +1,7 @@
#include "config.h"
#include <assert.h>
#include <stdint.h>
#include <ccan/mem/mem.h>
#include <ccan/tap/tap.h>
@ -96,8 +97,11 @@ int main(void)
haystack1 + sizeof(haystack1), 1));
ok1(!memoverlaps(haystack1 + sizeof(haystack1), 1,
haystack1, sizeof(haystack1)));
ok1(!memoverlaps(haystack1, sizeof(haystack1), haystack1 - 1, 1));
ok1(!memoverlaps(haystack1 - 1, 1, haystack1, sizeof(haystack1)));
/* Forming haystack1 - 1 directly is UB; round-trip via uintptr_t. */
ok1(!memoverlaps(haystack1, sizeof(haystack1),
(void *)((uintptr_t)haystack1 - 1), 1));
ok1(!memoverlaps((void *)((uintptr_t)haystack1 - 1), 1,
haystack1, sizeof(haystack1)));
ok1(memoverlaps(haystack1, 5, haystack1 + 4, 7));
ok1(!memoverlaps(haystack1, 5, haystack1 + 5, 6));
ok1(memoverlaps(haystack1 + 4, 7, haystack1, 5));

View file

@ -3,6 +3,7 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
void membuf_init_(struct membuf *mb,
void *elems, size_t num_elems, size_t elemsize,
@ -42,6 +43,12 @@ size_t membuf_prepare_space_(struct membuf *mb,
if (num_extra < mb->max_elems)
num_extra = mb->max_elems;
/* Don't let the allocation size wrap. */
if (num_extra > SIZE_MAX / elemsize - mb->max_elems) {
errno = ENOMEM;
return 0;
}
expand = mb->expandfn(mb, mb->elems,
(mb->max_elems + num_extra) * elemsize);
if (!expand) {
@ -51,6 +58,9 @@ size_t membuf_prepare_space_(struct membuf *mb,
mb->elems = expand;
}
}
/* Nothing moved if there was no old buffer. */
if (!oldstart)
return 0;
return (char *)membuf_elems_(mb, elemsize) - oldstart;
}

View file

@ -83,6 +83,8 @@ static inline size_t membuf_num_elems_(const struct membuf *mb)
static inline void *membuf_elems_(const struct membuf *mb, size_t elemsize)
{
if (!mb->elems)
return NULL;
return mb->elems + mb->start * elemsize;
}
@ -130,6 +132,8 @@ static inline size_t membuf_num_space_(const struct membuf *mb)
static inline void *membuf_space_(struct membuf *mb, size_t elemsize)
{
if (!mb->elems)
return NULL;
return mb->elems + mb->end * elemsize;
}

View file

@ -60,8 +60,7 @@ char *opt_set_charp(const char *arg, char **p)
return NULL;
}
/* Set an integer value, various forms.
FIXME: set to 1 on arg == NULL ? */
/* Set an integer value, various forms. */
char *opt_set_intval(const char *arg, int *i)
{
long l;

View file

@ -447,14 +447,14 @@ char *opt_set_charp(const char *arg, char **p);
/* If *p is NULL, this returns false (i.e. doesn't show a default) */
bool opt_show_charp(char *buf, size_t len, char *const *p);
/* Set an integer value, various forms. Sets to 1 on arg == NULL. */
char *opt_set_intval(const char *arg, int *i);
/* Set an integer value, various forms. */
char *opt_set_intval(const char *arg, int *i) NO_NULL_ARGS;
bool opt_show_intval(char *buf, size_t len, const int *i);
char *opt_set_uintval(const char *arg, unsigned int *ui);
char *opt_set_uintval(const char *arg, unsigned int *ui) NO_NULL_ARGS;
bool opt_show_uintval(char *buf, size_t len, const unsigned int *ui);
char *opt_set_longval(const char *arg, long *l);
char *opt_set_longval(const char *arg, long *l) NO_NULL_ARGS;
bool opt_show_longval(char *buf, size_t len, const long *l);
char *opt_set_ulongval(const char *arg, unsigned long *ul);
char *opt_set_ulongval(const char *arg, unsigned long *ul) NO_NULL_ARGS;
bool opt_show_ulongval(char *buf, size_t len, const unsigned long *ul);
/* Set an floating point value, various forms. */

View file

@ -92,12 +92,13 @@ int parse_one(int *argc, char *argv[], enum opt_type is_early, unsigned *offset,
arg = 1;
} else {
for (arg = 1; argv[arg]; arg++) {
if (argv[arg][0] == '-')
if (argv[arg][0] == '-' && argv[arg][1])
break;
}
}
if (!argv[arg] || argv[arg][0] != '-')
/* A bare '-' is an operand, not an option. */
if (!argv[arg] || argv[arg][0] != '-' || argv[arg][1] == '\0')
return 0;
/* Special arg terminator option. */

View file

@ -56,13 +56,13 @@ int main(void)
ok1(total_order_cmp(order2, &item2, &item2) == 0);
ok1(total_order_cmp(order2, &item3, &item3) == 0);
ok1(total_order_cmp(order2, &item1, &item2) == 1);
ok1(total_order_cmp(order2, &item1, &item2) == -1);
ok1(total_order_cmp(order2, &item2, &item3) == 1);
ok1(total_order_cmp(order2, &item1, &item3) == 1);
ok1(total_order_cmp(order2, &item2, &item1) == -1);
ok1(total_order_cmp(order2, &item2, &item1) == 1);
ok1(total_order_cmp(order2, &item3, &item2) == -1);
ok1(total_order_cmp(order2, &item3, &item1) == -1);
exit(0);
return exit_status();
}

View file

@ -54,6 +54,7 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
int child_close[4], num_child_close = 0;
pid_t childpid;
int err;
ssize_t r;
if (fd_tochild) {
if (fd_tochild == &pipecmd_preserve) {
@ -164,7 +165,9 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
if (write(execfail[1], &err, sizeof(err))) {
;
}
exit(127);
/* _exit: don't flush the parent's stdio buffers again,
* nor run its atexit handlers. */
_exit(127);
}
int i;
@ -172,9 +175,22 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
close(par_close[i]);
/* Child will close this without writing on successful exec. */
if (read(execfail[0], &err, sizeof(err)) == sizeof(err)) {
do {
r = read(execfail[0], &err, sizeof(err));
} while (r < 0 && errno == EINTR);
if (r == sizeof(err)) {
close(execfail[0]);
waitpid(childpid, NULL, 0);
/* Useless now: close the parent-side pipe ends too. */
if (fd_tochild && fd_tochild != &pipecmd_preserve)
close(tochild[1]);
if (fd_fromchild && fd_fromchild != &pipecmd_preserve)
close(fromchild[0]);
if (fd_errfromchild && fd_errfromchild != &pipecmd_preserve
&& fd_errfromchild != fd_fromchild)
close(errfromchild[0]);
while (waitpid(childpid, NULL, 0) < 0 && errno == EINTR)
;
errno = err;
return -1;
}
@ -190,6 +206,8 @@ pid_t pipecmdarr(int *fd_tochild, int *fd_fromchild, int *fd_errfromchild,
fail:
for (i = 0; i < num_par_close; i++)
close_noerr(par_close[i]);
for (i = 0; i < num_child_close; i++)
close_noerr(child_close[i]);
return -1;
}

View file

@ -28,9 +28,10 @@ static char *grab(const char *filename)
while ((ret = read(fd, buffer + s, max - s)) > 0) {
s += ret;
if (s == max) {
buffer = realloc(buffer, max*2+1);
if (!buffer)
goto close;
char *nb = realloc(buffer, max*2+1);
if (!nb)
goto free;
buffer = nb;
max *= 2;
}
}
@ -62,10 +63,14 @@ static struct ptr_valid_map *add_map(struct ptr_valid_map *map,
unsigned long start, unsigned long end, bool is_write)
{
if (*num == *max) {
struct ptr_valid_map *newmap;
*max *= 2;
map = realloc(map, sizeof(*map) * *max);
if (!map)
newmap = realloc(map, sizeof(*newmap) * *max);
if (!newmap) {
free(map);
return NULL;
}
map = newmap;
}
map[*num].start = (void *)start;
map[*num].end = (void *)end;
@ -182,9 +187,9 @@ static void run_child(int infd, int outfd)
/* This is weird. */
if (read(infd, &size, sizeof(size)) != sizeof(size))
exit(1);
_exit(1);
if (read(infd, &is_write, sizeof(is_write)) != sizeof(is_write))
exit(2);
_exit(2);
for (i = 0; i < size; i++) {
ret = p[i];
@ -194,9 +199,9 @@ static void run_child(int infd, int outfd)
/* If we're still here, the answer is "yes". */
if (write(outfd, &ret, 1) != 1)
exit(3);
_exit(3);
}
exit(0);
_exit(0);
}
static bool create_child(struct ptr_valid_batch *batch)
@ -271,16 +276,22 @@ bool ptr_valid_batch(struct ptr_valid_batch *batch,
char *start, *end;
bool ret;
if ((intptr_t)p & (alignment - 1))
if ((intptr_t)p & (alignment - 1)) {
errno = EFAULT;
return false;
}
start = (void *)((intptr_t)p & ~(getpagesize() - 1));
end = (void *)(((intptr_t)p + size - 1) & ~(getpagesize() - 1));
/* We cache single page hits. */
if (start == end) {
if (batch->last && batch->last == start)
if (batch->last && batch->last == start
&& batch->last_write == write) {
if (!batch->last_ok)
errno = EFAULT;
return batch->last_ok;
}
}
if (batch->num_maps)
@ -291,8 +302,11 @@ bool ptr_valid_batch(struct ptr_valid_batch *batch,
if (start == end) {
batch->last = start;
batch->last_ok = ret;
batch->last_write = write;
}
if (!ret)
errno = EFAULT;
return ret;
}

View file

@ -80,6 +80,7 @@ struct ptr_valid_batch {
int to_child, from_child;
void *last;
bool last_ok;
bool last_write;
};
/**

View file

@ -62,8 +62,14 @@ void *rbuf_fill_all(struct rbuf *rbuf)
void *rbuf_fill(struct rbuf *rbuf)
{
if (!rbuf_len(rbuf)) {
if (get_more(rbuf) < 0)
ssize_t r = get_more(rbuf);
if (r < 0)
return NULL;
/* EOF: documented NULL with errno 0. */
if (r == 0) {
errno = 0;
return NULL;
}
}
return rbuf_start(rbuf);
}

View file

@ -206,7 +206,8 @@ bool rune_condition_is_valid(enum rune_condition cond)
size_t rune_altern_fieldname_len(const char *alternstr, size_t alternstrlen)
{
for (size_t i = 0; i < alternstrlen; i++) {
if (cispunct(alternstr[i]) && alternstr[i] != '_')
if (cispunct(alternstr[i]) && alternstr[i] != '_'
&& alternstr[i] != '-' && alternstr[i] != '.')
return i;
}
return alternstrlen;

View file

@ -70,6 +70,10 @@ struct rune *rune_dup(const tal_t *ctx, const struct rune *rune TAKES)
return tal_steal(ctx, (struct rune *)rune);
dup = tal_dup(ctx, struct rune, rune);
if (rune->unique_id)
dup->unique_id = tal_strdup(dup, rune->unique_id);
if (rune->version)
dup->version = tal_strdup(dup, rune->version);
dup->restrs = tal_arr(dup, struct rune_restr *, tal_count(rune->restrs));
for (size_t i = 0; i < tal_count(rune->restrs); i++) {
dup->restrs[i] = rune_restr_dup(dup->restrs,
@ -287,11 +291,16 @@ static int lexo_order(const char *fieldval_str,
size_t fieldval_strlen,
const char *alt)
{
int ret = strncmp(fieldval_str, alt, fieldval_strlen);
size_t altlen = strlen(alt);
size_t minlen = fieldval_strlen < altlen ? fieldval_strlen : altlen;
int ret = memcmp(fieldval_str, alt, minlen);
/* If alt is same but longer, fieldval is < */
if (ret == 0 && strlen(alt) > fieldval_strlen)
ret = -1;
if (ret == 0) {
if (fieldval_strlen < altlen)
ret = -1;
else if (fieldval_strlen > altlen)
ret = 1;
}
return ret;
}

View file

@ -40,7 +40,7 @@
* *size_total += size;
* }
*
* #define EVALUATE(psx, short, pt, st, t) \
* #define EVALUATE(psx, sht, pt, st, t) \
* evaluate(sizeof(psx), stringify(psx), stringify(sht), pt, st, t)
*
* int main(void)

View file

@ -153,7 +153,7 @@ bool base32_encode(const void *buf, size_t bufsize, char *dest, size_t destsize)
destsize -= 8;
dest += 8;
}
if (destsize != 1)
if (destsize < 1)
return false;
*dest = '\0';
return true;

View file

@ -18,7 +18,7 @@
* for (i = 1; i < argc; i++) {
* char str[hex_str_size(strlen(argv[i]))];
*
* hex_encode(str, sizeof(str), argv[i], strlen(argv[i]));
* hex_encode(argv[i], strlen(argv[i]), str, sizeof(str));
* printf("%s ", str);
* }
* printf("\n");

View file

@ -5,6 +5,9 @@ size_t strcount(const char *haystack, const char *needle)
{
size_t i = 0, nlen = strlen(needle);
if (nlen == 0)
return 0;
while ((haystack = strstr(haystack, needle)) != NULL) {
i++;
haystack += nlen;

View file

@ -53,6 +53,7 @@ int main(int argc, char *argv[])
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/ilog\n"
"ccan/mem\n"
"ccan/short_types\n"
"ccan/str\n"
"ccan/tcon\n"

View file

@ -2,9 +2,11 @@
#include <ccan/strmap/strmap.h>
#include <ccan/short_types/short_types.h>
#include <ccan/str/str.h>
#include <ccan/mem/mem.h>
#include <ccan/ilog/ilog.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
struct node {
@ -42,7 +44,7 @@ void *strmap_getn_(const struct strmap *map,
/* Not empty map? */
if (map->u.n) {
n = closest((struct strmap *)map, member, memberlen);
if (!strncmp(member, n->u.s, memberlen) && !n->u.s[memberlen])
if (memeqstr(member, memberlen, n->u.s))
return n->v;
}
errno = ENOENT;
@ -178,26 +180,119 @@ char *strmap_del_(struct strmap *map, const char *member, void **valuep)
return (char *)ret;
}
static bool iterate(struct strmap n,
bool (*handle)(const char *, void *, void *),
const void *data)
/* Defer child[1] of a node we're descending past. */
static void iter_push(struct strmap_iter *it, struct strmap *slot)
{
if (n.v)
return handle(n.u.s, n.v, (void *)data);
if (STRMAP_NUM_ITER_PARENTS == 0) {
it->dropped = true;
return;
}
if (it->num_parents == STRMAP_NUM_ITER_PARENTS) {
/* Full: drop the *shallowest* deferral (kept in-order by
* the slow path once the stack runs out). */
memmove(&it->parents[0], &it->parents[1],
sizeof(it->parents[0]) * (STRMAP_NUM_ITER_PARENTS - 1));
it->num_parents--;
it->dropped = true;
}
it->parents[it->num_parents++] = slot;
}
return iterate(n.u.n->child[0], handle, data)
&& iterate(n.u.n->child[1], handle, data);
/* Descend leftmost from *slot, deferring child[1]s, and yield the leaf. */
static const char *iter_descend(struct strmap_iter *it, struct strmap *slot,
void **valuep)
{
while (!slot->v) {
iter_push(it, &slot->u.n->child[1]);
slot = &slot->u.n->child[0];
}
*valuep = slot->v;
return slot->u.s;
}
/* Successor of cur by value: O(depth), no stack. */
static const char *iter_successor(const struct strmap *map,
const char *cur, void **valuep)
{
size_t len = strlen(cur);
const u8 *bytes = (const u8 *)cur;
struct strmap n, cand;
bool have_cand = false;
n = *(struct strmap *)map;
while (!n.v) {
u8 c = 0, direction;
if (n.u.n->byte_num < len)
c = bytes[n.u.n->byte_num];
direction = (c >> n.u.n->bit_num) & 1;
if (direction == 0) {
/* Everything in child[1] sorts after child[0]. */
cand = n.u.n->child[1];
have_cand = true;
}
n = n.u.n->child[direction];
}
if (!have_cand)
return NULL;
/* Leftmost member of the deepest candidate subtree. */
while (!cand.v)
cand = cand.u.n->child[0];
*valuep = cand.v;
return cand.u.s;
}
const char *strmap_iter_first_(struct strmap_iter *it,
const struct strmap *map, void **valuep)
{
it->num_parents = 0;
it->dropped = false;
it->slow_mode = false;
if (!map->u.n)
return NULL;
return iter_descend(it, (struct strmap *)map, valuep);
}
const char *strmap_iter_next_(struct strmap_iter *it,
const struct strmap *map, const char *cur,
void **valuep)
{
struct strmap *slot;
if (!it->slow_mode) {
if (it->num_parents != 0) {
slot = it->parents[--it->num_parents];
return iter_descend(it, slot, valuep);
}
if (!it->dropped)
return NULL;
/* We dropped deferrals past STRMAP_NUM_ITER_PARENTS;
* from here on, find successors by re-descent. */
it->dropped = false;
it->slow_mode = true;
}
return iter_successor(map, cur, valuep);
}
void strmap_iterate_(const struct strmap *map,
bool (*handle)(const char *, void *, void *),
const void *data)
{
/* Empty map? */
if (!map->u.n)
return;
struct strmap_iter it;
const char *m;
void *v;
iterate(*map, handle, data);
for (m = strmap_iter_first_(&it, map, &v);
m;
m = strmap_iter_next_(&it, map, m, &v)) {
if (!handle(m, v, (void *)data))
break;
}
}
const struct strmap *strmap_prefix_(const struct strmap *map,
@ -235,6 +330,7 @@ const struct strmap *strmap_prefix_(const struct strmap *map,
return top;
}
/* Recursive fallback for strmap_clear_'s OOM path. */
static void clear(struct strmap n)
{
if (!n.v) {
@ -246,7 +342,75 @@ static void clear(struct strmap n)
void strmap_clear_(struct strmap *map)
{
if (map->u.n)
clear(*map);
uintptr_t inline_stack[STRMAP_NUM_ITER_PARENTS];
uintptr_t *stack = inline_stack;
size_t num = 0, max = STRMAP_NUM_ITER_PARENTS;
uintptr_t cur;
bool have_cur;
if (!map->u.n)
return;
/* Post-order without recursion: slot pointers tagged in their
* low bits (0 = visit child[0], 1 = visit child[1], 2 = free). */
cur = (uintptr_t)map;
have_cur = true;
while (have_cur || num) {
struct strmap *slot;
unsigned int tag;
if (!have_cur)
cur = stack[--num];
have_cur = false;
slot = (struct strmap *)(cur & ~(uintptr_t)3);
tag = cur & 3;
if (slot->v) {
/* Leaf: caller-owned, keep. */
continue;
}
if (tag == 2) {
free(slot->u.n);
continue;
}
/* tag 0 or 1: requeue for the next phase, then descend. */
{
struct strmap *child = &slot->u.n->child[tag];
if (num == max) {
uintptr_t *ns;
size_t nmax = max ? max * 2 : 64;
if (stack == inline_stack) {
ns = malloc(nmax * sizeof(*ns));
if (ns)
memcpy(ns, stack,
num * sizeof(*ns));
} else {
ns = realloc(stack, nmax * sizeof(*ns));
}
if (ns) {
stack = ns;
max = nmax;
} else {
/* OOM: finish this subtree
* recursively. */
clear(*child);
if (tag == 0)
clear(slot->u.n->child[1]);
free(slot->u.n);
continue;
}
}
stack[num++] = (uintptr_t)slot | (tag + 1);
cur = (uintptr_t)child;
have_cur = true;
}
}
if (stack != inline_stack)
free(stack);
map->u.n = NULL;
}

View file

@ -5,6 +5,7 @@
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
/**
* struct strmap - representation of a string map
@ -204,6 +205,75 @@ void strmap_iterate_(const struct strmap *map,
bool (*handle)(const char *, void *, void *),
const void *data);
#ifndef STRMAP_NUM_ITER_PARENTS
#define STRMAP_NUM_ITER_PARENTS 16
#endif
/**
* struct strmap_iter - state for strmap_iter_first/strmap_iter_next.
*
* This is exposed so you can declare it on the stack. It holds up to
* STRMAP_NUM_ITER_PARENTS deferred subtrees; in trees deeper than
* that, iteration falls back to re-descending from the root (an
* O(depth) successor search per step), so memory use stays bounded
* for arbitrarily deep trees.
*/
struct strmap_iter {
struct strmap *parents[STRMAP_NUM_ITER_PARENTS];
uint16_t num_parents;
bool dropped;
bool slow_mode;
};
/**
* strmap_iter_first - begin an ordered iteration over a map.
* @it: the iterator to initialize.
* @map: the typed strmap to iterate.
* @valuep: a pointer to a value to fill in.
*
* Returns the first member, and sets *@valuep, or returns NULL if the
* map is empty. You should not alter the map during iteration!
*
* Example:
* static void dump_map_iter(const STRMAP(int *) *map)
* {
* struct strmap_iter it;
* const char *m;
* int *v;
*
* for (m = strmap_iter_first(&it, map, &v);
* m;
* m = strmap_iter_next(&it, map, m, &v))
* printf("%s=>%i\n", m, *v);
* }
*/
#define strmap_iter_first(it, map, valuep) \
strmap_iter_first_((it), \
tcon_unwrap(tcon_check_ptr((map), canary, \
(valuep))), \
(void *)(valuep))
const char *strmap_iter_first_(struct strmap_iter *it,
const struct strmap *map, void **valuep);
/**
* strmap_iter_next - continue an ordered iteration.
* @it: the iterator, initialized by strmap_iter_first().
* @map: the typed strmap.
* @cur: the member the iteration is currently on.
* @valuep: a pointer to a value to fill in.
*
* Returns the next member, and sets *@valuep, or NULL at the end of
* the map.
*/
#define strmap_iter_next(it, map, cur, valuep) \
strmap_iter_next_((it), \
tcon_unwrap(tcon_check_ptr((map), canary, \
(valuep))), \
(cur), (void *)(valuep))
const char *strmap_iter_next_(struct strmap_iter *it,
const struct strmap *map, const char *cur,
void **valuep);
/**
* strmap_prefix - return a submap matching a prefix
* @map: the map.

View file

@ -19,6 +19,7 @@
#include <ccan/ilog/ilog.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
struct node {
@ -228,26 +229,127 @@ char *strset_del(struct strset *set, const char *member)
return (char *)ret;
}
static bool iterate(struct strset n,
bool (*handle)(const char *, void *), const void *data)
{
if (n.u.s[0])
return handle(n.u.s, (void *)data);
if (unlikely(n.u.n->byte_num == (size_t)-1))
return handle(n.u.n->child[0].u.s, (void *)data);
return iterate(n.u.n->child[0], handle, data)
&& iterate(n.u.n->child[1], handle, data);
}
void strset_iterate_(const struct strset *set,
bool (*handle)(const char *, void *), const void *data)
{
/* Empty set? */
if (!set->u.n)
return;
struct strset_iter it;
const char *m;
iterate(*set, handle, data);
for (m = strset_iter_first(&it, set);
m;
m = strset_iter_next(&it, set, m)) {
if (!handle(m, (void *)data))
break;
}
}
/* Defer child[1] of a node we're descending past. */
static void iter_push(struct strset_iter *it, struct strset *slot)
{
if (STRSET_NUM_ITER_PARENTS == 0) {
it->dropped = true;
return;
}
if (it->num_parents == STRSET_NUM_ITER_PARENTS) {
/* Full: drop the *shallowest* deferral (kept in-order by
* the slow_mode path once the stack runs out). */
memmove(&it->parents[0], &it->parents[1],
sizeof(it->parents[0]) * (STRSET_NUM_ITER_PARENTS - 1));
it->num_parents--;
it->dropped = true;
}
it->parents[it->num_parents++] = slot;
}
/* Descend leftmost from *slot, deferring child[1]s, and yield the leaf. */
static const char *iter_descend(struct strset_iter *it, struct strset *slot)
{
while (!slot->u.s[0]) {
/* Empty-string node: the string is child[0]. */
if (unlikely(slot->u.n->byte_num == (size_t)-1)) {
slot = &slot->u.n->child[0];
break;
}
iter_push(it, &slot->u.n->child[1]);
slot = &slot->u.n->child[0];
}
return slot->u.s;
}
/* Successor of cur by value: O(depth), no stack. */
static const char *iter_successor(const struct strset *set,
const char *cur)
{
size_t len = strlen(cur);
const u8 *bytes = (const u8 *)cur;
struct strset n, cand;
bool have_cand = false;
n = *(struct strset *)set;
while (!n.u.s[0]) {
u8 c = 0, direction;
/* Empty-string node: only holds "" in child[0]. */
if (unlikely(n.u.n->byte_num == (size_t)-1))
break;
if (n.u.n->byte_num < len)
c = bytes[n.u.n->byte_num];
direction = (c >> n.u.n->bit_num) & 1;
if (direction == 0) {
/* Everything in child[1] sorts after child[0]. */
cand = n.u.n->child[1];
have_cand = true;
}
n = n.u.n->child[direction];
}
if (!have_cand)
return NULL;
/* Leftmost member of the deepest candidate subtree. */
while (!cand.u.s[0]) {
if (unlikely(cand.u.n->byte_num == (size_t)-1)) {
cand = cand.u.n->child[0];
break;
}
cand = cand.u.n->child[0];
}
return cand.u.s;
}
const char *strset_iter_first(struct strset_iter *it,
const struct strset *set)
{
it->num_parents = 0;
it->dropped = false;
it->slow_mode = false;
if (!set->u.n)
return NULL;
return iter_descend(it, (struct strset *)set);
}
const char *strset_iter_next(struct strset_iter *it,
const struct strset *set,
const char *cur)
{
struct strset *slot;
if (likely(!it->slow_mode)) {
if (it->num_parents != 0) {
slot = it->parents[--it->num_parents];
return iter_descend(it, slot);
}
if (!it->dropped)
return NULL;
/* We dropped deferrals past STRSET_NUM_ITER_PARENTS;
* from here on, find successors by re-descent. */
it->dropped = false;
it->slow_mode = true;
}
return iter_successor(set, cur);
}
const struct strset *strset_prefix(const struct strset *set, const char *prefix)
@ -290,6 +392,7 @@ const struct strset *strset_prefix(const struct strset *set, const char *prefix)
return top;
}
/* Recursive fallback for strset_clear's OOM path. */
static void clear(struct strset n)
{
if (!n.u.s[0]) {
@ -303,7 +406,81 @@ static void clear(struct strset n)
void strset_clear(struct strset *set)
{
if (set->u.n)
clear(*set);
uintptr_t inline_stack[STRSET_NUM_ITER_PARENTS];
uintptr_t *stack = inline_stack;
size_t num = 0, max = STRSET_NUM_ITER_PARENTS;
uintptr_t cur;
bool have_cur;
if (!set->u.n)
return;
/* Post-order without recursion: slot pointers tagged in their
* low bits (0 = visit child[0], 1 = visit child[1], 2 = free). */
cur = (uintptr_t)set;
have_cur = true;
while (have_cur || num) {
struct strset *slot;
unsigned int tag;
if (!have_cur)
cur = stack[--num];
have_cur = false;
slot = (struct strset *)(cur & ~(uintptr_t)3);
tag = cur & 3;
if (slot->u.s[0]) {
/* Leaf: caller-owned, keep. */
continue;
}
if (unlikely(slot->u.n->byte_num == (size_t)-1)) {
/* Empty-string node: child[0] is the caller-owned
* string itself; child[1] is unused. */
free(slot->u.n);
continue;
}
if (tag == 2) {
free(slot->u.n);
continue;
}
/* tag 0 or 1: requeue for the next phase, then descend. */
{
struct strset *child = &slot->u.n->child[tag];
if (num == max) {
uintptr_t *ns;
size_t nmax = max ? max * 2 : 64;
if (stack == inline_stack) {
ns = malloc(nmax * sizeof(*ns));
if (ns)
memcpy(ns, stack,
num * sizeof(*ns));
} else {
ns = realloc(stack, nmax * sizeof(*ns));
}
if (ns) {
stack = ns;
max = nmax;
} else {
/* OOM: finish this subtree
* recursively. */
clear(*child);
if (tag == 0)
clear(slot->u.n->child[1]);
free(slot->u.n);
continue;
}
}
stack[num++] = (uintptr_t)slot | (tag + 1);
cur = (uintptr_t)child;
have_cur = true;
}
}
if (stack != inline_stack)
free(stack);
set->u.n = NULL;
}

View file

@ -4,6 +4,7 @@
#include <ccan/typesafe_cb/typesafe_cb.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
/**
* struct strset - representation of a string set
@ -141,6 +142,61 @@ void strset_clear(struct strset *set);
void strset_iterate_(const struct strset *set,
bool (*handle)(const char *, void *), const void *data);
#ifndef STRSET_NUM_ITER_PARENTS
#define STRSET_NUM_ITER_PARENTS 16
#endif
/**
* struct strset_iter - state for strset_iter_first/strset_iter_next.
*
* This is exposed so you can declare it on the stack. It holds up to
* STRSET_NUM_ITER_PARENTS deferred subtrees; in trees deeper than
* that, iteration falls back to re-descending from the root (an
* O(depth) successor search per step), so memory use stays bounded
* for arbitrarily deep trees.
*/
struct strset_iter {
struct strset *parents[STRSET_NUM_ITER_PARENTS];
uint16_t num_parents;
bool dropped;
bool slow_mode;
};
/**
* strset_iter_first - begin an ordered iteration over a set.
* @it: the iterator to initialize.
* @set: the set.
*
* Returns the first member, or NULL if the set is empty.
* You should not alter the set during iteration!
*
* Example:
* static void dump_set_iter(const struct strset *set)
* {
* struct strset_iter it;
* const char *m;
*
* for (m = strset_iter_first(&it, set);
* m;
* m = strset_iter_next(&it, set, m))
* printf("%s\n", m);
* }
*/
const char *strset_iter_first(struct strset_iter *it,
const struct strset *set);
/**
* strset_iter_next - continue an ordered iteration.
* @it: the iterator, initialized by strset_iter_first().
* @set: the set.
* @cur: the member the iteration is currently on.
*
* Returns the next member, or NULL at the end of the set.
*/
const char *strset_iter_next(struct strset_iter *it,
const struct strset *set,
const char *cur);
/**
* strset_prefix - return a subset matching a prefix

View file

@ -18,6 +18,10 @@
* there isn't any, or how many we expect. A negative number means
* "up to or equal to that amount of padding", as padding can be
* platform dependent.
*
* Note that members which are themselves structures or unions are
* compared with memcmp(), so any *internal* padding they contain can
* cause false negatives, just like top-level padding would.
*/
#define STRUCTEQ_DEF(sname, padbytes, ...) \
static inline bool CPPMAGIC_GLUE2(sname, _eq)(const struct sname *_a, \

View file

@ -81,6 +81,10 @@ bool taken(const void *p)
memmove(&takenarr[i-1], &takenarr[i],
(--num_taken - (i - 1))*sizeof(takenarr[0]));
if (labelarr) {
memmove(&labelarr[i-1], &labelarr[i],
(num_taken - (i - 1))*sizeof(labelarr[0]));
}
return true;
}
@ -114,6 +118,7 @@ const char *taken_any(void)
void take_cleanup(void)
{
max_taken = num_taken = 0;
allocfail = 0;
free(takenarr);
takenarr = NULL;
free(labelarr);

View file

@ -17,9 +17,11 @@ static void *grab_fd_internal(const void *ctx, int fd, bool add_nul_term)
size = 0;
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode))
if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size != 0)
max = st.st_size;
else
/* Non-regular file, or one reporting a zero size despite
* having content (eg. /proc, /sys): guess and grow. */
max = 16384;
buffer = tal_arr(ctx, char, max+add_nul_term);

View file

@ -11,6 +11,9 @@
* The object will be freed when @newobj is freed or the last tal_link()
* is tal_delink'ed.
*
* Note: @newobj must not be tal_free()'d or tal_steal()'d while it has
* links: that aborts. Remove all links first.
*
* Returns @newobj or NULL (if an allocation fails).
*
* Example:

View file

@ -321,7 +321,8 @@ fail_take_to:
break;
}
if (!tal_resize(&ret, maxlen *= 2 + 1))
maxlen = maxlen * 2 + 1;
if (!tal_resize(&ret, maxlen))
goto fail;
}
@ -393,8 +394,12 @@ char *path_simplify(const tal_t *ctx, const char *path)
j = sep - ret + 1;
else
j = 0;
continue;
}
continue;
/* Symlink or nonexistent: can't safely step
* back, so keep the ".." literally. */
ret[j-1] = PATH_SEP;
goto copy;
} else if (start) {
/* /.. => / */
j = 1;
@ -436,20 +441,27 @@ char *path_basename(const tal_t *ctx, const char *path)
/* Trailing slashes need to be trimmed. */
if (!sep[1]) {
const char *end;
size_t end, sep_off;
for (end = sep; end != path; end--)
if (*end != PATH_SEP)
for (end = sep - path; end != 0; end--)
if (path[end] != PATH_SEP)
break;
/* Find *previous* / */
for (sep = end; sep >= path && *sep != PATH_SEP; sep--);
/* Find *previous* / ((size_t)-1 if there is none) */
for (sep_off = end; ; sep_off--) {
if (path[sep_off] == PATH_SEP)
break;
if (sep_off == 0) {
sep_off = (size_t)-1;
break;
}
}
/* All /? Just return / */
if (end == sep)
if (end == sep_off)
ret = tal_strdup(ctx, PATH_SEP_STR);
else
ret = tal_strndup(ctx, sep+1, end - sep);
ret = tal_strndup(ctx, path + (sep_off + 1), end - sep_off);
} else
ret = tal_strdup(ctx, sep + 1);

View file

@ -52,6 +52,9 @@ char *path_simplify(const tal_t *ctx, const char *a TAKES);
*
* If @a is an absolute path, return a copy of it. Otherwise, attach
* @a to @base.
*
* @base and @a must not be NULL, except as the taken result of a
* failed take() chain (e.g. path_join(ctx, take(tal_fmt(...)), "x")).
*/
char *path_join(const tal_t *ctx, const char *base TAKES, const char *a TAKES);

View file

@ -6,7 +6,7 @@ int main(void)
{
char cwd[1024], *path, *ctx = tal_strdup(NULL, "ctx");
plan_tests(85);
plan_tests(95);
if (!getcwd(cwd, sizeof(cwd)))
abort();
@ -227,6 +227,33 @@ int main(void)
ok1(tal_parent(path) == ctx);
tal_free(path);
/* Don't trace back over a symlink: keep ".." literally. */
path = path_simplify(ctx, "run-simplify-link/..");
ok1(streq(path, "run-simplify-link/.."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "run-simplify-link/../");
ok1(streq(path, "run-simplify-link/.."));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "run-simplify-link/../x");
ok1(streq(path, "run-simplify-link/../x"));
ok1(tal_parent(path) == ctx);
tal_free(path);
path = path_simplify(ctx, "run-simplify-link/../../foo");
ok1(streq(path, "run-simplify-link/../../foo"));
ok1(tal_parent(path) == ctx);
tal_free(path);
/* Nonexistent path: can't prove it's a real dir, keep "..". */
path = path_simplify(ctx, "run-simplify-nosuch/..");
ok1(streq(path, "run-simplify-nosuch/.."));
ok1(tal_parent(path) == ctx);
tal_free(path);
/* take tests */
path = path_simplify(ctx, take(tal_strdup(ctx, "/tmp/../tmp/.")));
ok1(streq(path, "/tmp"));

View file

@ -150,8 +150,11 @@ char **tal_strsplit_(const tal_t *ctx,
if (flags == STR_EMPTY_OK && dlen)
dlen = 1;
str += len + dlen;
if (++num == max && !tal_resize(&parts, max*=2 + 1))
goto fail;
if (++num == max) {
max = max * 2 + 1;
if (!tal_resize(&parts, max))
goto fail;
}
}
parts[num] = NULL;
@ -214,33 +217,9 @@ fail:
goto out;
}
static size_t count_open_braces(const char *string)
{
#if 1
size_t num = 0, esc = 0;
while (*string) {
if (*string == '\\')
esc++;
else {
/* An odd number of \ means it's escaped. */
if (*string == '(' && (esc & 1) == 0)
num++;
esc = 0;
}
string++;
}
return num;
#else
return strcount(string, "(");
#endif
}
bool tal_strreg_(const tal_t *ctx, const char *string, const char *label,
const char *regex, ...)
{
size_t nmatch = 1 + count_open_braces(regex);
regmatch_t matches[nmatch];
regex_t r;
bool ret = false;
unsigned int i;
@ -249,33 +228,39 @@ bool tal_strreg_(const tal_t *ctx, const char *string, const char *label,
if (regcomp(&r, regex, REG_EXTENDED) != 0)
goto fail_no_re;
if (regexec(&r, string, nmatch, matches, 0) != 0)
goto fail;
{
/* re_nsub counts real capture groups: unlike scanning the
* regex text, it is not fooled by '(' inside bracket
* expressions. */
size_t nmatch = 1 + r.re_nsub;
regmatch_t matches[nmatch];
ret = true;
va_start(ap, regex);
for (i = 1; i < nmatch; i++) {
char **arg = va_arg(ap, char **);
if (arg) {
/* eg. ([a-z])? can give "no match". */
if (matches[i].rm_so == -1)
*arg = NULL;
else {
*arg = tal_strndup_(ctx,
string + matches[i].rm_so,
matches[i].rm_eo
- matches[i].rm_so,
label);
/* FIXME: If we fail, we set some and leak! */
if (!*arg) {
ret = false;
break;
if (regexec(&r, string, nmatch, matches, 0) == 0) {
ret = true;
va_start(ap, regex);
for (i = 1; i < nmatch; i++) {
char **arg = va_arg(ap, char **);
if (arg) {
/* eg. ([a-z])? can give "no match". */
if (matches[i].rm_so == -1)
*arg = NULL;
else {
*arg = tal_strndup_(ctx,
string + matches[i].rm_so,
matches[i].rm_eo
- matches[i].rm_so,
label);
/* FIXME: If we fail, we set some and leak! */
if (!*arg) {
ret = false;
break;
}
}
}
}
va_end(ap);
}
}
va_end(ap);
fail:
regfree(&r);
fail_no_re:
if (taken(regex))

View file

@ -244,6 +244,9 @@ static void notify(const struct tal_hdr *ctx,
EXTRA_ARG(n));
else
cb.destroy(from_tal_hdr(ctx));
/* Restore: object may have been rescued by a
* tal_steal() from inside the destructor. */
n->u = cb;
} else
n->u.notifyfn(from_tal_hdr_or_null(ctx), type,
(void *)info);
@ -420,22 +423,26 @@ static bool add_child(struct tal_hdr *parent, struct tal_hdr *child)
return true;
}
static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno);
/* Free t's children, properties and t itself. The destroying bit is
* already set (by del_tree() or tal_free()). */
static void del_tree_inner(struct tal_hdr *t, const tal_t *orig,
int saved_errno)
{
struct prop_hdr *prop;
char *ptr, *next;
assert(!taken(from_tal_hdr(t)));
/* Already being destroyed? Don't loop. */
if (unlikely(get_destroying_bit(t->parent_child)))
return;
set_destroying_bit(&t->parent_child);
/* Call free notifiers. */
notify(t, TAL_NOTIFY_FREE, (tal_t *)orig, saved_errno);
/* A destructor/notifier can rescue the object by tal_steal()ing
* it elsewhere: add_child() clears the destroying bit. */
if (!get_destroying_bit(t->parent_child))
return;
/* Now free children and groups. */
prop = find_property(t, CHILDREN);
if (prop) {
@ -456,6 +463,16 @@ static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
freefn(t);
}
static void del_tree(struct tal_hdr *t, const tal_t *orig, int saved_errno)
{
/* Already being destroyed? Don't loop. */
if (unlikely(get_destroying_bit(t->parent_child)))
return;
set_destroying_bit(&t->parent_child);
del_tree_inner(t, orig, saved_errno);
}
/* Don't have compiler complain we're returning NULL if we promised not to! */
static void *null_alloc_failed(void)
{
@ -525,11 +542,21 @@ void *tal_free(const tal_t *ctx)
t = debug_tal(to_tal_hdr(ctx));
if (unlikely(get_destroying_bit(t->parent_child)))
return NULL;
/* Unlink and mark destroying before notifying the parent:
* a notifier which (recursively) calls tal_free() on ctx
* is then a no-op rather than unbounded recursion. */
list_del(&t->list);
set_destroying_bit(&t->parent_child);
if (notifiers)
notify(ignore_destroying_bit(t->parent_child)->parent,
TAL_NOTIFY_DEL_CHILD, ctx, saved_errno);
list_del(&t->list);
del_tree(t, ctx, saved_errno);
/* A notifier can rescue ctx by tal_steal()ing it elsewhere:
* add_child() clears the destroying bit. */
if (!get_destroying_bit(t->parent_child)) {
errno = saved_errno;
return NULL;
}
del_tree_inner(t, ctx, saved_errno);
errno = saved_errno;
}
return NULL;
@ -543,11 +570,28 @@ void *tal_steal_(const tal_t *new_parent, const tal_t *ctx)
newpar = debug_tal(to_tal_hdr_or_null(new_parent));
t = debug_tal(to_tal_hdr(ctx));
/* Unlink it from old parent. */
list_del(&t->list);
/* Can't steal into a parent which is being destroyed:
* we'd be linked into the list del_tree() is draining,
* and freed (or looped on) anyway. */
if (unlikely(get_destroying_bit(newpar->parent_child)))
return NULL;
/* Unlink it from old parent (an object being destroyed
* has already been unlinked: it can only be stolen as a
* rescue from its destructor/notifier). */
if (!get_destroying_bit(t->parent_child))
list_del(&t->list);
old_parent = ignore_destroying_bit(t->parent_child)->parent;
if (unlikely(!add_child(newpar, t))) {
/* No fallback for a rescue from a destructor:
* re-linking into the old parent would silently
* keep the object (its destructor saw NULL), and if
* the old parent is mid-del_tree it would re-enter
* the list being drained. Leave it unlinked and
* destroying; the free proceeds. */
if (get_destroying_bit(t->parent_child))
return NULL;
/* We can always add to old parent, because it has a
* children property already. */
if (!add_child(old_parent, t))
@ -800,6 +844,12 @@ bool tal_expand_(tal_t **ctxp, const void *src, size_t size, size_t count)
old_len = debug_tal(to_tal_hdr(*ctxp))->bytelen;
/* Check for multiplicative overflow */
if (size && unlikely(count * size / size != count)) {
call_error("dup size overflow");
goto out;
}
/* Check for additive overflow */
if (old_len + count * size < old_len) {
call_error("dup size overflow");
@ -810,7 +860,9 @@ bool tal_expand_(tal_t **ctxp, const void *src, size_t size, size_t count)
assert(src < *ctxp
|| (char *)src >= (char *)(*ctxp) + old_len);
if (!tal_resize_(ctxp, size, old_len/size + count, false))
/* Resize by raw length, so excess bytes are preserved and
* tal_count() grows by exactly count. */
if (!tal_resize_(ctxp, 1, old_len + count * size, false))
goto out;
memcpy((char *)*ctxp + old_len, src, count * size);

View file

@ -143,7 +143,16 @@ void *tal_free(const tal_t *p);
*
* This may need to perform an allocation, in which case it may fail; thus
* it can return NULL, otherwise returns @ptr. If @ptr is NULL, this function does
* nothing.
* nothing. It also fails (returning NULL) if @ctx is currently being
* destroyed.
*
* A destructor or TAL_NOTIFY_FREE notifier may use this to rescue @ptr
* from destruction by moving it to a new parent; the free is then
* aborted. The rescue is only noticed once every notifier and
* destructor registered for this pass has run, so later ones still
* fire (and all of them fire again when the object is finally
* destroyed). If the rescue fails (allocation failure), the free
* proceeds.
*/
#if HAVE_STATEMENT_EXPR
/* Weird macro avoids gcc's 'warning: value computed is not used'. */
@ -174,7 +183,8 @@ void *tal_free(const tal_t *p);
*
* If @function has not been successfully added as a destructor, this returns
* false. Note that if we're inside the destructor call itself, this will
* return false.
* return false, and the destructor remains registered: it is restored when
* the call returns, in case the object was rescued from destruction.
*/
#define tal_del_destructor(ptr, function) \
tal_del_destructor_((ptr), typesafe_cb(void, void *, (function), (ptr)))
@ -206,7 +216,8 @@ void *tal_free(const tal_t *p);
*
* If @function has not been successfully added as a destructor, this returns
* false. Note that if we're inside the destructor call itself, this will
* return false.
* return false, and the destructor remains registered: it is restored when
* the call returns, in case the object was rescued from destruction.
*/
#define tal_del_destructor(ptr, function) \
tal_del_destructor_((ptr), typesafe_cb(void, void *, (function), (ptr)))
@ -272,6 +283,17 @@ enum tal_notify_type {
* not called when this context is tal_free()d: TAL_NOTIFY_FREE is
* considered sufficient for that case.
*
* For TAL_NOTIFY_ADD_CHILD, the callback must not tal_free() or
* tal_steal() the child: the allocating call will still return it to
* the caller, so this will crash or corrupt.
*
* For TAL_NOTIFY_DEL_CHILD, the child is already unlinked and marked
* destroying: calling tal_free() on it from the callback is a no-op,
* and tal_steal()ing it rescues it from destruction (aborting the
* free).
*
* In all cases, the callback must not tal_free() @ptr itself.
*
* TAL_NOTIFY_ADD_NOTIFIER/TAL_NOTIFIER_DEL_NOTIFIER are called when a
* notifier is added or removed (not for this notifier): @info is the
* callback. This is also called for tal_add_destructor and

View file

@ -127,6 +127,10 @@
*
* It evaluates to @x so you can chain it.
*
* Note that a @expr of type void * silently passes against any
* canary (the comparison is legal C); the same applies to
* tcon_check_ptr().
*
* Example:
* #define tlist_add(h, n, member) \
* list_add(&tcon_check((h), canary, (n))->raw, &(n)->member)

View file

@ -2,6 +2,17 @@
#include <ccan/time/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <limits.h>
/* Largest representable tv_sec (time_t is signed). */
#define TIMEREL_SEC_MAX \
((time_t)((~(uint64_t)0) >> (64 - sizeof(time_t)*CHAR_BIT + 1)))
static struct timerel timerel_max(void)
{
struct timerel max = { { TIMEREL_SEC_MAX, 999999999 } };
return max;
}
#if !HAVE_CLOCK_GETTIME
#include <sys/time.h>
@ -58,6 +69,11 @@ struct timerel time_divide(struct timerel t, unsigned long div)
/* FIXME: fp is cheating! */
double nsec = rem * 1000000000.0 + t.ts.tv_nsec;
res.ts.tv_nsec = nsec / div;
/* Rounding can give exactly 1e9; renormalize. */
if (res.ts.tv_nsec >= 1000000000) {
res.ts.tv_nsec -= 1000000000;
res.ts.tv_sec++;
}
} else {
ns = rem * 1000000000 + t.ts.tv_nsec;
res.ts.tv_nsec = ns / div;
@ -69,11 +85,16 @@ struct timerel time_multiply(struct timerel t, unsigned long mult)
{
struct timerel res;
(void)TIMEREL_CHECK(t);
/* Are we going to overflow if we multiply nsec? */
if (mult & ~((1UL << 30) - 1)) {
/* FIXME: fp is cheating! */
double nsec = (double)t.ts.tv_nsec * mult;
/* Saturate rather than overflow time_t. */
if (nsec >= (double)TIMEREL_SEC_MAX * 1000000000.0)
return timerel_max();
res.ts.tv_sec = nsec / 1000000000.0;
res.ts.tv_nsec = nsec - (res.ts.tv_sec * 1000000000.0);
} else {
@ -82,7 +103,12 @@ struct timerel time_multiply(struct timerel t, unsigned long mult)
res.ts.tv_nsec = nsec % 1000000000;
res.ts.tv_sec = nsec / 1000000000;
}
res.ts.tv_sec += TIMEREL_CHECK(t).ts.tv_sec * mult;
/* The seconds multiply can overflow too: saturate. */
if (mult != 0
&& t.ts.tv_sec > (time_t)((TIMEREL_SEC_MAX - res.ts.tv_sec) / mult))
return timerel_max();
res.ts.tv_sec += t.ts.tv_sec * mult;
return TIMEREL_CHECK(res);
}

View file

@ -569,6 +569,9 @@ struct timerel time_divide(struct timerel t, unsigned long div);
* @t: a relative time.
* @mult: number to multiply it by.
*
* If the result would not fit in a timerel, the maximum representable
* time is returned.
*
* Example:
* ...
* printf("Time to do 100000 forks would be %u sec\n",

View file

@ -153,6 +153,15 @@ static void timers_far_get(struct timers *timers,
}
}
/* Maximum time covered by levels 0..@level (from base); saturates
* once the range exceeds the representable 64 bits. */
static uint64_t level_max_time(const struct timers *timers, unsigned int level)
{
if ((level + 1) * TIMER_LEVEL_BITS >= 64)
return -1ULL;
return timers->base + (1ULL << ((level+1)*TIMER_LEVEL_BITS)) - 1;
}
static void add_level(struct timers *timers, unsigned int level)
{
struct timer_level *l;
@ -169,8 +178,7 @@ static void add_level(struct timers *timers, unsigned int level)
timers->level[level] = l;
list_head_init(&from_far);
timers_far_get(timers, &from_far,
timers->base + (1ULL << ((level+1)*TIMER_LEVEL_BITS)) - 1);
timers_far_get(timers, &from_far, level_max_time(timers, level));
while ((t = list_pop(&from_far, struct timer, list)) != NULL)
timer_add_raw(timers, t);
@ -313,8 +321,7 @@ static void timer_fast_forward(struct timers *timers, uint64_t time)
if (!timers->level[level]) {
/* We need any which belong on this level. */
timers_far_get(timers, &list,
timers->base
+ (1ULL << ((level+1)*TIMER_LEVEL_BITS))-1);
level_max_time(timers, level));
need_level = level;
} else {
unsigned src;
@ -353,6 +360,9 @@ struct timer *timers_expire(struct timers *timers, struct timemono expire)
if (list_empty(&timers->far))
return NULL;
add_level(timers, 0);
/* Allocation failure: timers wait safely on the far list. */
if (!timers->level[0])
return NULL;
}
do {
@ -448,8 +458,13 @@ struct timers *timers_check(const struct timers *timers, const char *abortstr)
}
past_levels:
base = (timers->base & ~((1ULL << (TIMER_LEVEL_BITS * l)) - 1))
+ (1ULL << (TIMER_LEVEL_BITS * l)) - 1;
if (TIMER_LEVEL_BITS * l < 64) {
base = (timers->base & ~((1ULL << (TIMER_LEVEL_BITS * l)) - 1))
+ (1ULL << (TIMER_LEVEL_BITS * l)) - 1;
} else {
/* Levels cover all representable times: far must be empty. */
base = -1ULL;
}
if (!timer_list_check(&timers->far, base, -1ULL,
timers->firsts[ARRAY_SIZE(timers->level)],
abortstr))

View file

@ -10,7 +10,7 @@ void _callback(void (*fn)(void *arg), void *arg)
/* Callback is set up to warn if arg isn't a pointer (since it won't
* pass cleanly to _callback's second arg. */
#define callback(fn, arg) \
_callback(typesafe_cb(void, (fn), (arg)), (arg))
_callback(typesafe_cb(void, void *, (fn), (arg)), (arg))
void my_callback(int something);
void my_callback(int something)
@ -23,6 +23,9 @@ int main(void)
#ifdef FAIL
/* This fails due to arg, not due to cast. */
callback(my_callback, 100);
#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P
#error "Unfortunately we don't fail if typesafe_cb is a noop."
#endif
#endif
return 0;
}

View file

@ -78,6 +78,11 @@
* It is assumed that @arg is of pointer type: usually @arg is passed
* or assigned to a void * elsewhere anyway.
*
* Note that the callback must have a prototype: an unprototyped
* function (ie. "void fn()") defeats this check entirely, and is
* silently accepted with any @arg type (gcc and clang give no
* diagnostic at -Wall).
*
* Example:
* void _register_callback(void (*fn)(void *arg), void *arg);
* #define register_callback(fn, arg) \

View file

@ -396,6 +396,9 @@ static const struct test base_tests[] = {
{ "HAVE_STATEMENT_EXPR", "statement expression support",
"INSIDE_MAIN", NULL, NULL,
"return ({ int x = argc; x == argc ? 0 : 1; });" },
{ "HAVE_STATIC_ASSERT", "_Static_assert support",
"INSIDE_MAIN", NULL, NULL,
"_Static_assert(1, \"OK\"); return 0;" },
{ "HAVE_SYS_FILIO_H", "<sys/filio.h>",
"OUTSIDE_MAIN", NULL, NULL, /* Solaris needs this for FIONREAD */
"#include <sys/filio.h>\n" },