Indeed. Looking at the generated assembly code (from ./autogen.sh && ./configure && make on Ubuntu x86_64), and analyzing function secp256k1_ecmult_const from src/libsecp256k1_la-secp256k1.o), index is actually pushed to a stack location. In Ghidra, it looks like this (when rsp + 8 is used):
<img width="1556" height="883" alt="image" src="https://github.com/user-attachments/assets/89895ff4-4727-43c0-b965-c1baee84c124" />
But adding secp256k1_memclear_explicit(&index, sizeof(index)); does not seem to actually clear this copy. Generating the assembly code with gcc -DPACKAGE_NAME=\"libsecp256k1\" -DPACKAGE_TARNAME=\"libsecp256k1\" -DPACKAGE_VERSION=\"0.7.2-dev\" "-DPACKAGE_STRING=\"libsecp256k1 0.7.2-dev\"" -DPACKAGE_BUGREPORT=\"https://github.com/bitcoin-core/secp256k1/issues\" -DPACKAGE_URL=\"https://github.com/bitcoin-core/secp256k1\" -DPACKAGE=\"libsecp256k1\" -DVERSION=\"0.7.2-dev\" -DHAVE_STDIO_H=1 -DHAVE_STDLIB_H=1 -DHAVE_STRING_H=1 -DHAVE_INTTYPES_H=1 -DHAVE_STDINT_H=1 -DHAVE_STRINGS_H=1 -DHAVE_SYS_STAT_H=1 -DHAVE_SYS_TYPES_H=1 -DHAVE_UNISTD_H=1 -DSTDC_HEADERS=1 -DHAVE_DLFCN_H=1 -DLT_OBJDIR=\".libs/\" -DHAVE_SYS_TYPES_H=1 -DHAVE_SYS_WAIT_H=1 -DHAVE_UNISTD_H=1 -I. -DUSE_ASM_X86_64=1 -DECMULT_WINDOW_SIZE=15 -DCOMB_BLOCKS=43 -DCOMB_TEETH=6 -DENABLE_MODULE_ELLSWIFT=1 -DENABLE_MODULE_MUSIG=1 -DENABLE_MODULE_SCHNORRSIG=1 -DENABLE_MODULE_EXTRAKEYS=1 -DENABLE_MODULE_ECDH=1 -O2 -std=c89 -pedantic -Wno-long-long -Wnested-externs -Wshadow -Wstrict-prototypes -Wundef -Wno-overlength-strings -Wall -Wno-unused-function -Wextra -Wcast-align -Wcast-align=strict -g -O2 -MT src/libsecp256k1_la-secp256k1.lo -MD -MP -MF src/.deps/libsecp256k1_la-secp256k1.Tpo -c src/secp256k1.c -S -o src/libsecp256k1_la-secp256k1.s (GCC version gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0 on Ubuntu 22.04) shows:
.LBE54234:
.loc 26 251 9 view .LVU48389
movl %eax, 32(%rsp) ; store index to rsp+32
...
.LBE54228:
.LBE54236:
.loc 26 251 9 is_stmt 1 discriminator 3 view .LVU48407
xorl %r8d, %r8d
cmpl %r12d, 32(%rsp) ; compare the index with m
.LBB54238:
.loc 3 356 44 is_stmt 0 discriminator 3 view .LVU48408
movq (%rdx), %r13
.LBE54238:
.LBE54237:
.loc 26 251 9 discriminator 3 view .LVU48409
sete %r8b
...
.LBE54245:
.LBE54248:
.loc 26 251 9 is_stmt 1 discriminator 3 view .LVU48487
.loc 26 251 9 discriminator 3 view .LVU48488
cmpl $16, %r12d
jne .L697 ; end of for (m = 1 ; ...) loop
movq %rax, 440(%rsp)
movq 8(%rsp), %rsi
movq (%rsp), %rax
movq %r10, 408(%rsp)
movq %rbp, 416(%rsp)
movq %rax, 368(%rsp)
.loc 26 251 9 view .LVU48489
.LVL6784:
.LBB54249:
.LBI54249:
.loc 8 269 30 view .LVU48490
.loc 8 271 5 view .LVU48491
.LBB54250:
.LBI54250:
.loc 8 237 30 view .LVU48492
.LBB54251:
.loc 8 253 5 view .LVU48493
.loc 8 254 5 is_stmt 0 view .LVU48494
movq 64(%rsp), %rax
movq %r9, 424(%rsp)
movq %rbx, 432(%rsp)
movq %rsi, 400(%rsp)
movq %r11, 392(%rsp)
movq %r14, 384(%rsp)
movq %r15, 376(%rsp)
movl $0, 148(%rsp) ; rsp+148 gets cleared, not rsp+32
It feels a bit weird that GCC is not clearing the right 32-bit stack location, and this might be a bug in an old version of GCC, or an error in my analysis of the assembly code.
Anyway, I don't have much more time to spend on debugging this and if you want to clear index, please go ahead :)