BRK(2)

πŸ’… BRK(2) β€” bestie we need to talk about brk, sbrk πŸ’…

okay so this is page BRK(2) from the System Calls Manual (yes, section 2, we're in syscall territory, very serious, very grown-up) πŸŽ€


✨ NAME ✨

brk, sbrk β€” change data segment size


πŸ“š LIBRARY πŸ“š

Standard C Library (libc, -lc)


🌸 SYNOPSIS 🌸

#include <unistd.h>

int
brk(const void *addr);

void *
sbrk(intptr_t incr);

πŸ¦‹ DESCRIPTION πŸ¦‹

so. brk() and sbrk() are legacy interfaces β€” like, from before modern virtual memory management even existed. they're deprecated and they're not present on the arm64 or riscv architectures. if you wanna allocate pages, bestie, the move is mmap(2). use that. periodt.

what do they actually do tho? they change how much memory is allocated in a process's data segment, by moving the location of the β€œbreak”. and the break is the first address after the end of the process's uninitialized data segment (also known as the β€œBSS”). iykyk.

  • brk() β€” sets the break to addr.
  • sbrk() β€” raises the break by incr bytes, so you're allocating at least incr bytes of new memory in the data segment. and if incr is negative? the break gets lowered by incr bytes. we love a bidirectional king πŸ’…

πŸ“ NOTES πŸ“

ngl the kernel is out here growing and shrinking the actual process data segment size only in page sizes, but these functions will let you set the break to unaligned values β€” meaning it can point to any address inside the last page of the data segment. quirky of them fr fr.

wanna know the current value of the program break? just call sbrk(0). slay. also check out end(3) bestie.

oh and getrlimit(2) is the syscall you want for figuring out the maximum permissible size of the data segment. you will not be able to set the break beyond etext + rlim.rlim_max, where that rlim.rlim_max value comes back from a call to getrlimit(RLIMIT_DATA, &rlim). (for the definition of etext, go see end(3).)


🫢 RETURN VALUES 🫢

  • brk() returns the value 0 if it slayed; otherwise the value -1 comes back and the global variable errno is set to indicate the error. 😭
  • sbrk() returns the prior break value if it worked; otherwise the value (void *)-1 is returned and the global variable errno is set to indicate the error. not the vibe.

πŸ’€ ERRORS πŸ’€

the brk() and sbrk() functions will fail if:

  • [EINVAL] β€” the requested break value was beyond the beginning of the data segment. girl. πŸ™ˆ
  • [ENOMEM] β€” the data segment size limit, as set by setrlimit(2), was exceeded.
  • [ENOMEM] β€” there wasn't enough space in the swap area to support the expansion of the data segment.

πŸ‘‘ SEE ALSO πŸ‘‘

execve(2), getrlimit(2), mmap(2), end(3), free(3), malloc(3)


🌈 HISTORY 🌈

brk() appeared in Version 7 AT&T UNIX β€” ancient. iconic. main character energy from birth. ✨

then FreeBSD 11.0 introduced the arm64 and riscv architectures, which do not support brk() or sbrk() at all. they simply said no πŸ’…


πŸ“ BUGS πŸ“

okay this is the part where you actually pay attention bestie, these are real:
  • mixing brk() or sbrk() with malloc(3), free(3), or similar functions will result in non-portable program behavior. do not do this. it's giving chaos.
  • setting the break may fail due to a temporary lack of swap space. and you literally can't distinguish that from a failure caused by exceeding the maximum size of the data segment unless you go consult getrlimit(2). so rude.
  • sbrk() is sometimes used to monitor heap use by calling it with an argument of 0. but the result is unlikely to reflect actual utilization when it's combined with an mmap(2)-based malloc. sad!
  • brk() and sbrk() are not thread-safe. 🚨

okay that's the whole page, love that for us πŸ’– this came from Debian, dated June 2, 2018, and it's page BRK(2) β€” mwah, take care of yourselves, go use mmap(2) instead πŸŽ€βœ¨πŸ¦‹

πŸ“¦ where this page comes from