Submit #754589: janet-lang janet 4dd08a4 Heap-based Buffer Overflowinfo

Titlejanet-lang janet 4dd08a4 Heap-based Buffer Overflow
Description### Description We discovered a Heap-buffer-overflow vulnerability in Janet. The crash occurs in the handleattr function during the compilation phase, specifically when processing definition attributes. The ASAN report indicates a READ violation of size 8, occurring exactly at the boundary (0 bytes after) of a 40-byte allocated region (likely a Tuple of size 5). Vendor confirmed and fixed this vulnerability in commit [2fabc80](https://github.com/janet-lang/janet/commit/2fabc80151a2b8834ee59cda8a70453f848b40e5). ### Environment - OS: Linux x86_64 - Complier: Clang - Build Configuration: Release mode with ASan enabled. ### Vulnerability Details - Target: Janet (janet-lang) - Vulnerability Type: CWE-125: Out-of-bounds Read - Function: handleattr - Location: src/core/specials.c:311 (Called by janetc_def) - Root Cause Analysis: The function handleattr parses attributes for definitions (likely accessing elements of a source tuple). The ASAN report shows: ``` 0x504000021138 is located 0 bytes after 40-byte region ``` This implies handleattr is accessing an index equal to the size of the tuple (e.g., accessing index 5 of a size-5 tuple), causing an off-by-one read overflow. This typically happens when the compiler expects a specific syntax structure (like a key-value pair or a specific number of arguments) but the input provides a malformed structure that passes initial checks but fails during specific attribute handling. ### Reproduce 1. Build janet and harness with Release optimization and ASAN enabled. <details> <summary>harness.c</summary> ``` #include "janet.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <stdint.h> #include <string.h> int main(int argc, char **argv) { if (argc < 2) { return 1; } janet_init(); JanetTable *env = janet_core_env(NULL); FILE *f = fopen(argv[1], "rb"); if (!f) { janet_deinit(); return 1; } fseek(f, 0, SEEK_END); long len = ftell(f); fseek(f, 0, SEEK_SET); unsigned char *buf = (unsigned char *)malloc(len + 1); if (!buf) { fclose(f); janet_deinit(); return 1; } if (fread(buf, 1, len, f) != len) { free(buf); fclose(f); janet_deinit(); return 1; } fclose(f); buf[len] = '\0'; if (len >= 1) { Janet retval; janet_dostring(env, (const char *)buf, NULL, &retval); janet_gcroot(janet_wrap_nil()); } free(buf); janet_deinit(); return 0; } ``` </details> 2. Run with the crashing [file](https://github.com/oneafter/0123/blob/main/ja1/repro): ``` ./harness repro ``` <details> <summary>ASAN report</summary> ``` ==50040==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x504000021138 at pc 0x55b2717ba589 bp 0x7ffeee6fe8a0 sp 0x7ffeee6fe898 READ of size 8 at 0x504000021138 thread T0 #0 0x55b2717ba588 in handleattr /src/janet/src/core/specials.c:311:32 #1 0x55b2717a8959 in janetc_def /src/janet/src/core/specials.c:545:30 #2 0x55b2715ee736 in janetc_value /src/janet/src/core/compile.c:822:15 #3 0x55b2717aed2b in janetc_fn /src/janet/src/core/specials.c:1131:13 #4 0x55b2715ee736 in janetc_value /src/janet/src/core/compile.c:822:15 #5 0x55b2716ffdce in dohead_destructure /src/janet/src/core/specials.c:400:21 #6 0x55b2717a8b0a in janetc_def /src/janet/src/core/specials.c:552:12 #7 0x55b2715ee736 in janetc_value /src/janet/src/core/compile.c:822:15 #8 0x55b2715fb35e in janet_compile_lint /src/janet/src/core/compile.c:1081:5 #9 0x55b2716fc3dd in janet_compile /src/janet/src/core/compile.c:1099:12 #10 0x55b2716fc3dd in janet_dobytes /src/janet/src/core/run.c:51:39 #11 0x55b27159d0f3 in main /src/janet/harness.c:44:9 #12 0x7f4026cc51c9 (/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e) #13 0x7f4026cc528a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 274eec488d230825a136fa9c4d85370fed7a0a5e) #14 0x55b2714bba94 in _start (/src/janet/harness+0x54a94) (BuildId: 99073a3a75c69a7f87afa4e4c777fe183943214e) 0x504000021138 is located 0 bytes after 40-byte region [0x504000021110,0x504000021138) allocated by thread T0 here: #0 0x55b27155b8c3 in malloc (/src/janet/harness+0xf48c3) (BuildId: 99073a3a75c69a7f87afa4e4c777fe183943214e) #1 0x55b27159dcd2 in janet_gcalloc /src/janet/src/core/gc.c:536:11 SUMMARY: AddressSanitizer: heap-buffer-overflow /src/janet/src/core/specials.c:311:32 in handleattr Shadow bytes around the buggy address: 0x504000020e80: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 0x504000020f00: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 0x504000020f80: fa fa 00 00 00 00 00 00 fa fa 00 00 00 00 00 00 0x504000021000: fa fa 00 00 00 00 03 fa fa fa 00 00 00 00 00 00 0x504000021080: fa fa fd fd fd fd fd fd fa fa 00 00 00 00 00 fa =>0x504000021100: fa fa 00 00 00 00 00[fa]fa fa 00 00 00 00 00 00 0x504000021180: fa fa 00 00 00 00 00 fa fa fa 00 00 00 00 00 00 0x504000021200: fa fa 00 00 00 00 00 00 fa fa fa fa fa fa fa fa 0x504000021280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x504000021300: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa 0x504000021380: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa Shadow byte legend (one shadow byte represents 8 application bytes): Addressable: 00 Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: fa Freed heap region: fd Stack left redzone: f1 Stack mid redzone: f2 Stack right redzone: f3 Stack after return: f5 Stack use after scope: f8 Global redzone: f9 Global init order: f6 Poisoned by user: f7 Container overflow: fc Array cookie: ac Intra object redzone: bb ASan internal: fe Left alloca redzone: ca Right alloca redzone: cb ==50040==ABORTING ``` </details>
Source⚠️ https://github.com/janet-lang/janet/issues/1699
User Oneafter (UID 92781)
Submission02/09/2026 11:03 (2 months ago)
Moderation02/20/2026 15:34 (11 days later)
StatusAccepted
VulDB entry347106 [janet-lang janet up to 1.40.1 handleattr src/core/specials.c janetc_varset out-of-bounds]
Points20

Interested in the pricing of exploits?

See the underground prices here!