#2667·darknet

darknet rnn valid: trailing -seed without a value causes NULL seed dereference (strlen) in valid_char_rnn

Author: trustqqCreated Sep 11, 2026Updated Sep 11, 2026

Summary

With darknet rnn valid cfg/rnn.cfg -file <value> -seed (a trailing -seed with no value), find_char_arg() (src/utils.c:163-175) returns NULL instead of the default "\n\n": the preceding -file <value> has already been consumed by two del_arg() calls, pushing -seed into argv[4], and its value slot argv[5] is the NULL sentinel left by del_arg; moreover argc is passed by value and never decremented (utils.c:113-118), so the i < argc-1 check is ineffective. The NULL is passed straight through run_char_rnn() (examples/rnn.c:526, 537) into valid_char_rnn(), which executes strlen(seed) at rnn.c:446 → SIGSEGV. A standalone trailing -seed (without a preceding -file) does not trigger the bug — in that case -seed sits at argv[4], i == argc-1 does not satisfy i < argc-1, and the default value is returned.

Reproduction

bash
# Build (in the darknet-master/ directory; CPU-only build, zero external dependencies):
#   make -j$(nproc) DEBUG=1     # DEBUG=1 adds -O0 -g; the default DEBUG=0 build crashes the same way
#
# Run:
./darknet rnn valid cfg/rnn.cfg -file data/shakespeare.txt -seed < /dev/null

Note: the -file value does not need to exist (the valid subcommand never opens the file, it only consumes two argument slots); weights are not loaded (weights=(argc>4)?argv[4]:0 = NULL is a valid usage), so no weights need to be downloaded.

Source

c
/* src/utils.c:113-118 — shift left + set the trailing slot to a NULL sentinel;
 * argc is passed by value and never decremented */
void del_arg(int argc, char **argv, int index)
{
    int i;
    for(i = index; i < argc-1; ++i) argv[i] = argv[i+1];
    argv[i] = 0;
}

/* src/utils.c:163-175 — no NULL guard on the value slot */
char *find_char_arg(int argc, char **argv, char *arg, char *def)
{
    int i;
    for(i = 0; i < argc-1; ++i){
        if(!argv[i]) continue;
        if(0==strcmp(argv[i], arg)){
            def = argv[i+1];              /* the value slot may be the NULL sentinel left by del_arg */
            del_arg(argc, argv, i);
            del_arg(argc, argv, i);
            break;
        }
    }
    return def;                           /* returns NULL */
}
c
/* examples/rnn.c:526 — reads -seed; 537 — passes it straight through */
    char *seed = find_char_arg(argc, argv, "-seed", "\n\n");   /* line 526 */
    ...
    else if(0==strcmp(argv[2], "valid")) valid_char_rnn(cfg, weights, seed);  /* line 537 */

/* examples/rnn.c:446 — sink */
    int len = strlen(seed);               /* crashes when seed==NULL */

Result

Image