Skip to content

sysrand leaks threadvar variables #48

Description

@n5m

Calling sysrand.randomBytes causes Valgrind to mark memory as "possibly lost."

Example

# proof.nim
import nimcrypto/sysrand

var x: int
discard randomBytes(addr(x), sizeof(x))

Compile

$ nim c --gc:arc -d:useMalloc proof.nim 
Hint: used config file '/home/user/nim/config/nim.cfg' [Conf]
Hint: used config file '/home/user/nim/config/config.nims' [Conf]
....................
Hint:  [Link]
Hint: 41955 lines; 0.729s; 49.098MiB peakmem; Debug build; proj: /home/user/nimcrypto/proof.nim; out: /home/user/nimcrypto/proof [SuccessX]

Run

$ valgrind --error-exitcode=1 --leak-check=yes ./proof
[[Elided output]]
==989== LEAK SUMMARY:
==989==    definitely lost: 0 bytes in 0 blocks
==989==    indirectly lost: 0 bytes in 0 blocks
==989==      possibly lost: 24 bytes in 1 blocks
==989==    still reachable: 0 bytes in 0 blocks
==989==         suppressed: 0 bytes in 0 blocks
==989== 
==989== For counts of detected and suppressed errors, rerun with: -v
==989== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

This "leak" happens because the global RNG is marked as {.threadvar.}:

var gSystemRng {.threadvar.}: SystemRng ## System thread global RNG

but this seems like marking the variable as {.threadvar.} is avoidable entirely. After examining the way gSystemRng is initialized (when being compiled on Linux),

proc newSystemRNG(): SystemRng =
result = SystemRng()
if SYS_getrandom != 0:
var data: int
result.getRandomPresent = true
let res = syscall(SYS_getrandom, addr data, 1, GRND_NONBLOCK)
if res == -1:
let err = osLastError().int32
if err == ENOSYS or err == EPERM:
result.getRandomPresent = false
proc getSystemRNG(): SystemRng =
if gSystemRng.isNil: gSystemRng = newSystemRng()
result = gSystemRng

it seems that we could have

 var gSystemRng: SystemRng = newSystemRNG() ## System thread global RNG 

which does not have {.threadvar.}. Are there any issues I am overlooking with this approach?

I agree that this isn't a genuine leak since it is a global variable and the memory always still has a reference, but having Valgrind not complain would be a nice feature to have.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions