| // Copyright 2025 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package main | |
| import ( | |
| "internal/syscall/unix" | |
| "runtime" | |
| ) | |
| func init() { | |
| register("LockOSThreadVgetrandom", LockOSThreadVgetrandom) | |
| } | |
| var sinkInt = 1 | |
| func LockOSThreadVgetrandom() { | |
| // This is a regression test for https://go.dev/issue/73141. When that | |
| // reproduces, this crashes with SIGSEGV with no output or stack trace, | |
| // and detail only available in a core file. | |
| // | |
| // Thread exit via mexit cleans up vgetrandom state. Stress test thread | |
| // exit + vgetrandom to look for issues by creating lots of threads | |
| // that use GetRandom and then exit. | |
| // Launch at most 100 threads at a time. | |
| const parallelism = 100 | |
| ch := make(chan struct{}, parallelism) | |
| for range 100 { | |
| ch <- struct{}{} | |
| } | |
| // Create at most 1000 threads to avoid completely exhausting the | |
| // system. This test generally reproduces https://go.dev/issue/73141 in | |
| // less than 500 iterations. | |
| const iterations = 1000 | |
| for range iterations { | |
| <-ch | |
| go func() { | |
| defer func() { | |
| ch <- struct{}{} | |
| }() | |
| // Exit with LockOSThread held. | |
| runtime.LockOSThread() | |
| // Be sure to use GetRandom to initialize vgetrandom state. | |
| b := make([]byte, 1) | |
| _, err := unix.GetRandom(b, 0) | |
| if err != nil { | |
| panic(err) | |
| } | |
| // Do some busy-work. It is unclear why this is | |
| // necessary to reproduce. Perhaps to introduce | |
| // interesting scheduling where threads get descheduled | |
| // in the middle of getting or putting vgetrandom | |
| // state. | |
| i := 0 | |
| for range 10 * 1000 * 1000 { | |
| i += sinkInt | |
| } | |
| }() | |
| } | |
| // Wait for all threads to finish. | |
| for range parallelism { | |
| <-ch | |
| } | |
| println("OK") | |
| } | |