mem_brk_musl.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. // Copyright 2023 The Libc Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //go:build libc.membrk && !libc.memgrind && linux && (amd64 || arm64 || loong64 || ppc64le || s390x || riscv64 || 386 || arm)
  5. // This is a debug-only version of the memory handling functions. When a
  6. // program is built with -tags=libc.membrk a simple but safe version of malloc
  7. // and friends is used that works like sbrk(2). Additionally free becomes a
  8. // nop.
  9. // The fixed heap is initially filled with random bytes from a full cycle PRNG,
  10. // program startup time is substantially prolonged.
  11. package libc // import "modernc.org/libc"
  12. import (
  13. "fmt"
  14. "math"
  15. "math/bits"
  16. "runtime"
  17. "strings"
  18. "time"
  19. "unsafe"
  20. "modernc.org/mathutil"
  21. )
  22. const (
  23. isMemBrk = true
  24. heapSize = 1 << 30
  25. )
  26. var (
  27. brkIndex uintptr
  28. heap [heapSize]byte
  29. heapP uintptr
  30. heap0 uintptr
  31. heapRecords []heapRecord
  32. heapUsable = map[uintptr]Tsize_t{}
  33. heapFree = map[uintptr]struct{}{}
  34. rng *mathutil.FC32
  35. )
  36. type heapRecord struct {
  37. p uintptr
  38. pc uintptr
  39. }
  40. func (r *heapRecord) String() string {
  41. return fmt.Sprintf("[p=%#0x usable=%v pc=%s]", r.p, Xmalloc_usable_size(nil, r.p), pc2origin(r.pc))
  42. }
  43. func init() {
  44. if roundup(heapGuard, heapAlign) != heapGuard {
  45. panic("internal error")
  46. }
  47. heap0 = uintptr(unsafe.Pointer(&heap[0]))
  48. heapP = roundup(heap0, heapAlign)
  49. var err error
  50. if rng, err = mathutil.NewFC32(math.MinInt32, math.MaxInt32, true); err != nil {
  51. panic(err)
  52. }
  53. rng.Seed(time.Now().UnixNano())
  54. for i := range heap {
  55. heap[i] = byte(rng.Next())
  56. }
  57. }
  58. func pc2origin(pc uintptr) string {
  59. f := runtime.FuncForPC(pc)
  60. var fn, fns string
  61. var fl int
  62. if f != nil {
  63. fn, fl = f.FileLine(pc)
  64. fns = f.Name()
  65. if x := strings.LastIndex(fns, "."); x > 0 {
  66. fns = fns[x+1:]
  67. }
  68. }
  69. return fmt.Sprintf("%s:%d:%s", fn, fl, fns)
  70. }
  71. func malloc0(tls *TLS, pc uintptr, n0 Tsize_t, zero bool) (r uintptr) {
  72. usable := roundup(uintptr(n0), heapAlign)
  73. rq := usable + 2*heapGuard
  74. if brkIndex+rq > uintptr(len(heap)) {
  75. tls.setErrno(ENOMEM)
  76. return 0
  77. }
  78. r, brkIndex = heapP+brkIndex, brkIndex+rq
  79. heapRecords = append(heapRecords, heapRecord{p: r, pc: pc})
  80. r += heapGuard
  81. heapUsable[r] = Tsize_t(usable)
  82. if zero {
  83. n := uintptr(n0)
  84. for i := uintptr(0); i < n; i++ {
  85. *(*byte)(unsafe.Pointer(r + i)) = 0
  86. }
  87. }
  88. return r
  89. }
  90. func Xmalloc(tls *TLS, n Tsize_t) (r uintptr) {
  91. if __ccgo_strace {
  92. trc("tls=%v n=%v, (%v:)", tls, n, origin(2))
  93. defer func() { trc("-> %v", r) }()
  94. }
  95. if n > math.MaxInt {
  96. tls.setErrno(ENOMEM)
  97. return 0
  98. }
  99. if n == 0 {
  100. // malloc(0) should return unique pointers
  101. // (often expected and gnulib replaces malloc if malloc(0) returns 0)
  102. n = 1
  103. }
  104. allocatorMu.Lock()
  105. defer allocatorMu.Unlock()
  106. pc, _, _, _ := runtime.Caller(1)
  107. return malloc0(tls, pc, n, false)
  108. }
  109. func Xcalloc(tls *TLS, m Tsize_t, n Tsize_t) (r uintptr) {
  110. if __ccgo_strace {
  111. trc("tls=%v m=%v n=%v, (%v:)", tls, m, n, origin(2))
  112. defer func() { trc("-> %v", r) }()
  113. }
  114. hi, rq := bits.Mul(uint(m), uint(n))
  115. if hi != 0 || rq > math.MaxInt {
  116. tls.setErrno(ENOMEM)
  117. return 0
  118. }
  119. if rq == 0 {
  120. rq = 1
  121. }
  122. allocatorMu.Lock()
  123. defer allocatorMu.Unlock()
  124. pc, _, _, _ := runtime.Caller(1)
  125. return malloc0(tls, pc, Tsize_t(rq), true)
  126. }
  127. func Xrealloc(tls *TLS, p uintptr, n Tsize_t) (r uintptr) {
  128. if __ccgo_strace {
  129. trc("tls=%v p=%v n=%v, (%v:)", tls, p, n, origin(2))
  130. defer func() { trc("-> %v", r) }()
  131. }
  132. if n == 0 {
  133. Xfree(tls, p)
  134. return 0
  135. }
  136. allocatorMu.Lock()
  137. defer allocatorMu.Unlock()
  138. pc, _, _, _ := runtime.Caller(1)
  139. if p == 0 {
  140. return malloc0(tls, pc, n, false)
  141. }
  142. usable := heapUsable[p]
  143. if usable == 0 {
  144. panic(todo("realloc of unallocated memory: %#0x", p))
  145. }
  146. if usable >= n { // in place
  147. return p
  148. }
  149. // malloc
  150. r = malloc0(tls, pc, n, false)
  151. copy(unsafe.Slice((*byte)(unsafe.Pointer(r)), usable), unsafe.Slice((*byte)(unsafe.Pointer(p)), usable))
  152. Xfree(tls, p)
  153. return r
  154. }
  155. func Xfree(tls *TLS, p uintptr) {
  156. if __ccgo_strace {
  157. trc("tls=%v p=%v, (%v:)", tls, p, origin(2))
  158. }
  159. allocatorMu.Lock()
  160. defer allocatorMu.Unlock()
  161. if p == 0 {
  162. return
  163. }
  164. if _, ok := heapUsable[p]; !ok {
  165. panic(todo("free of unallocated memory: %#0x", p))
  166. }
  167. if _, ok := heapFree[p]; ok {
  168. panic(todo("double free: %#0x", p))
  169. }
  170. heapFree[p] = struct{}{}
  171. }
  172. func Xmalloc_usable_size(tls *TLS, p uintptr) (r Tsize_t) {
  173. if __ccgo_strace {
  174. trc("tls=%v p=%v, (%v:)", tls, p, origin(2))
  175. defer func() { trc("-> %v", r) }()
  176. }
  177. if p == 0 {
  178. return 0
  179. }
  180. allocatorMu.Lock()
  181. defer allocatorMu.Unlock()
  182. return heapUsable[p]
  183. }
  184. func MemAudit() (r []*MemAuditError) {
  185. allocatorMu.Lock()
  186. defer allocatorMu.Unlock()
  187. a := heapRecords
  188. auditP := heap0
  189. rng.Seek(0)
  190. for _, v := range a {
  191. heapP := v.p
  192. mallocP := heapP + heapGuard
  193. usable := heapUsable[mallocP]
  194. for ; auditP < mallocP; auditP++ {
  195. if g, e := *(*byte)(unsafe.Pointer(auditP)), byte(rng.Next()); g != e {
  196. r = append(r, &MemAuditError{Caller: pc2origin(v.pc), Message: fmt.Sprintf("guard area before %#0x, %v is corrupted at %#0x, got %#02x, expected %#02x", mallocP, usable, auditP, g, e)})
  197. }
  198. }
  199. for i := 0; Tsize_t(i) < usable; i++ {
  200. rng.Next()
  201. }
  202. auditP = mallocP + uintptr(usable)
  203. z := roundup(auditP, heapAlign)
  204. z += heapGuard
  205. for ; auditP < z; auditP++ {
  206. if g, e := *(*byte)(unsafe.Pointer(auditP)), byte(rng.Next()); g != e {
  207. r = append(r, &MemAuditError{Caller: pc2origin(v.pc), Message: fmt.Sprintf("guard area after %#0x, %v is corrupted at %#0x, got %#02x, expected %#02x", mallocP, usable, auditP, g, e)})
  208. }
  209. }
  210. }
  211. z := heap0 + uintptr(len(heap))
  212. for ; auditP < z; auditP++ {
  213. if g, e := *(*byte)(unsafe.Pointer(auditP)), byte(rng.Next()); g != e {
  214. r = append(r, &MemAuditError{Caller: "-", Message: fmt.Sprintf("guard area after used heap is corrupted at %#0x, got %#02x, expected %#02x", auditP, g, e)})
  215. return r // Report only the first fail
  216. }
  217. }
  218. return r
  219. }
  220. func UsableSize(p uintptr) Tsize_t {
  221. if p == 0 {
  222. return 0
  223. }
  224. allocatorMu.Lock()
  225. defer allocatorMu.Unlock()
  226. return heapUsable[p]
  227. }
  228. type MemAllocatorStat struct {
  229. Allocs int
  230. Bytes int
  231. Mmaps int
  232. }
  233. // MemStat no-op for this build tag
  234. func MemStat() MemAllocatorStat {
  235. return MemAllocatorStat{}
  236. }
  237. // MemAuditStart locks the memory allocator, initializes and enables memory
  238. // auditing. Finaly it unlocks the memory allocator.
  239. //
  240. // Some memory handling errors, like double free or freeing of unallocated
  241. // memory, will panic when memory auditing is enabled.
  242. //
  243. // This memory auditing functionality has to be enabled using the libc.memgrind
  244. // build tag.
  245. //
  246. // It is intended only for debug/test builds. It slows down memory allocation
  247. // routines and it has additional memory costs.
  248. func MemAuditStart() {}
  249. // MemAuditReport locks the memory allocator, reports memory leaks, if any.
  250. // Finally it disables memory auditing and unlocks the memory allocator.
  251. //
  252. // This memory auditing functionality has to be enabled using the libc.memgrind
  253. // build tag.
  254. //
  255. // It is intended only for debug/test builds. It slows down memory allocation
  256. // routines and it has additional memory costs.
  257. func MemAuditReport() error { return nil }