{"record":{"id":"608fa86dd7ce0b8c","repo":"nginx/nginx","slug":"ngx-log-alert-608fa8","errorCode":"NGX_LOG_ALERT","errorMessage":"quic token buffer is too small","messagePattern":"quic token buffer is too small","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/event/quic/ngx_event_quic_tokens.c","lineNumber":75,"sourceCode":"    p = ngx_cpymem(p, &exp, sizeof(time_t));\n\n    *p++ = is_retry ? 1 : 0;\n\n    if (odcid) {\n        *p++ = odcid->len;\n        p = ngx_cpymem(p, odcid->data, odcid->len);\n\n    } else {\n        *p++ = 0;\n    }\n\n    len = p - in;\n\n    cipher = EVP_aes_256_gcm();\n    iv_len = NGX_QUIC_AES_256_GCM_IV_LEN;\n\n    if ((size_t) (iv_len + len + NGX_QUIC_AES_256_GCM_TAG_LEN) > token->len) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0, \"quic token buffer is too small\");\n        return NGX_ERROR;\n    }\n\n    ctx = EVP_CIPHER_CTX_new();\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    iv = token->data;\n\n    if (RAND_bytes(iv, iv_len) <= 0\n        || !EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))\n    {\n        EVP_CIPHER_CTX_free(ctx);\n        return NGX_ERROR;\n    }\n\n    token->len = iv_len;","sourceCodeStart":57,"sourceCodeEnd":93,"githubUrl":"https://github.com/nginx/nginx/blob/3f6f7824d4e2eb1ac37dec76683d525ac0ff521c/src/event/quic/ngx_event_quic_tokens.c#L57-L93","documentation":"ngx_quic_new_token() encrypts the token plaintext (20-byte address hash, time_t expiry, retry flag, ODCID length + bytes, all bounded by NGX_QUIC_MAX_TOKEN_SIZE = 64) with AES-256-GCM into a caller-provided buffer; before touching crypto it verifies that IV (12 bytes) + plaintext + GCM tag (16 bytes) fits token->len, else it logs this ALERT and returns NGX_ERROR, which fails NEW_TOKEN / retry-token generation. Stock callers in ngx_event_quic_output.c pass a buffer of NGX_QUIC_TOKEN_BUF_SIZE (12 + 64 + 16 = 92), so a hit means a custom caller passed a smaller buffer, local patches changed sizes, or an unusual ABI (e.g. larger time_t).","triggerScenarios":"Third-party or patched code calling ngx_quic_new_token() with token->len < 12 + 20 + sizeof(time_t) + 2 + odcid->len + 16; local patches enlarging the ODCID or expiry fields; platforms with sizeof(time_t) > 8.","commonSituations":"Custom modules built into nginx that generate tokens; forks that resized token fields without resizing the buffer.","solutions":["Size token buffers to NGX_QUIC_TOKEN_BUF_SIZE and set token->len = NGX_QUIC_TOKEN_BUF_SIZE before the call","Do not shrink or reshape token fields (time_t size, ODCID length cap) in local patches","If stock nginx hits this on your platform, report it with ABI details (sizeof(time_t), NGX_QUIC_MAX_TOKEN_SIZE)"],"exampleFix":"/* before */\nu_char  buf[32];\nngx_str_t  token = { 32, buf };\n\n/* after */\nu_char  buf[NGX_QUIC_TOKEN_BUF_SIZE];\nngx_str_t  token = { NGX_QUIC_TOKEN_BUF_SIZE, buf };","handlingStrategy":"validation","validationCode":"if (token.len < (size_t) (NGX_QUIC_AES_256_GCM_IV_LEN + 20 + sizeof(time_t) + 2 + NGX_QUIC_MAX_CID_LEN + NGX_QUIC_AES_256_GCM_TAG_LEN)) {\n    /* grow the buffer before calling ngx_quic_new_token() */\n}","typeGuard":"#define ngx_quic_token_buf_ok(t) ((t)->len >= (size_t) NGX_QUIC_TOKEN_BUF_SIZE)","tryCatchPattern":"rc = ngx_quic_new_token(log, sa, socklen, key, &token, &odcid, exp, is_retry);\nif (rc != NGX_OK) { /* logged as ALERT; skip NEW_TOKEN/retry for this connection */ }","preventionTips":["Always allocate token buffers as NGX_QUIC_TOKEN_BUF_SIZE (IV + max payload + tag)","Keep token field sizes (time_t, ODCID cap) unchanged in local patches","Unit-test custom token callers with the maximum-size ODCID"],"tags":["nginx","quic","token","aes-gcm","buffer-size","retry"],"backgroundTag":"buffer-size-validation","analyzedSha":"3f6f7824d4e2eb1ac37dec76683d525ac0ff521c","analyzedAt":"2026-08-22T03:09:46.447Z","contentChangedAt":"2026-08-22T03:09:46.447Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}