On Tue, Mar 10, 2020 at 03:04:00AM +0530, Neeraj Pal wrote:
> Hi there,
>
> I am reading and learning the internals of malloc(3).
> So, after compiling the debug version of libc and using it for one
> basic sample code for malloc(3).
>
> Not able to understand some parts of the following code snippet:
>
> void
> _malloc_init(int from_rthreads)
> {
> u_int i, nmutexes;
> struct dir_info *d;
>
> _MALLOC_LOCK(1);
> if (!from_rthreads && mopts.malloc_pool[1]) {
> _MALLOC_UNLOCK(1);
> return;
> }
> if (!mopts.malloc_canary)
> omalloc_init();
>
> nmutexes = from_rthreads ? mopts.malloc_mutexes : 2;
> if (((uintptr_t)&malloc_readonly & MALLOC_PAGEMASK) == 0)
> mprotect(&malloc_readonly, sizeof(malloc_readonly),
> PROT_READ | PROT_WRITE);
> for (i = 0; i < nmutexes; i++) {
> if (mopts.malloc_pool[i])
> continue;
> if (i == 0) {
> omalloc_poolinit(&d, MAP_CONCEAL);
> d->malloc_junk = 2;
> d->malloc_cache = 0;
> } else {
> omalloc_poolinit(&d, 0);
> d->malloc_junk = mopts.def_malloc_junk;
> d->malloc_cache = mopts.def_malloc_cache;
> }
> d->mutex = i;
> mopts.malloc_pool[i] = d;
> }
>
> if (from_rthreads)
> mopts.malloc_mt = 1;
> else
> mopts.internal_funcs = 1;
>
> /*
> * Options have been set and will never be reset.
> * Prevent further tampering with them.
> */
> if (((uintptr_t)&malloc_readonly & MALLOC_PAGEMASK) == 0)
> mprotect(&malloc_readonly, sizeof(malloc_readonly), PROT_READ);
> _MALLOC_UNLOCK(1);
> }
>
> In the above code snippet, could some please through some light on the
> following queries
> 1. Use of nmutexes?
> 2. And, why it is looping till nmutexes and calls function
> omalloc_poolinit(&d, MAP_CONCEAL) /* when i == 0*/
> and other calls to omalloc_poolinit(&d, 0) /* when i != 0 */
> So, suppose in the case of nmutexes = 2, I am not sure where are the
> uses of these 3 initialized pools, that is, malloc_pool[0],
> malloc_pool[1] and malloc_pool[2]?
There's an off by one in your question :-)
Fo single threaded programs, two malloc_dir pools are maintained.
One for MAP_CONCEALED memory (#0) and one for regular (#1).
For multi-threaded porgram more pools are created. This is to avoid contention,
accesses to diffrent pools can run concurently.
>
>
> static void
> omalloc_poolinit(struct dir_info **dp, int mmap_flag)
> {
> char *p;
> size_t d_avail, regioninfo_size;
> struct dir_info *d;
> int i, j;
>
> /*
> * Allocate dir_info with a guard page on either side. Also
> * randomise offset inside the page at which the dir_info
> * lies (subject to alignment by 1 << MALLOC_MINSHIFT)
> */
> if ((p = MMAPNONE(DIR_INFO_RSZ + (MALLOC_PAGESIZE * 2), mmap_flag)) ==
> MAP_FAILED)
> wrterror(NULL, "malloc init mmap failed");
> mprotect(p + MALLOC_PAGESIZE, DIR_INFO_RSZ, PROT_READ | PROT_WRITE);
> d_avail = (DIR_INFO_RSZ - sizeof(*d)) >> MALLOC_MINSHIFT;
> d = (struct dir_info *)(p + MALLOC_PAGESIZE +
> (arc4random_uniform(d_avail) << MALLOC_MINSHIFT));
>
> rbytes_init(d);
> d->regions_free = d->regions_total = MALLOC_INITIAL_REGIONS;
> regioninfo_size = d->regions_total * sizeof(struct region_info);
> d->r = MMAP(regioninfo_size, mmap_flag);
> if (d->r == MAP_FAILED) {
> d->regions_total = 0;
> wrterror(NULL, "malloc init mmap failed");
> }
> for (i = 0; i <= MALLOC_MAXSHIFT; i++) {
> LIST_INIT(&d->chunk_info_list[i]);
> for (j = 0; j < MALLOC_CHUNK_LISTS; j++)
> LIST_INIT(&d->chunk_dir[i][j]);
> ...
> ...
> ...
> In the above code, inside function omalloc_poolinit(), first, it will
> allocate dir_info structure with a guard page on *both sides* like
> [guard page] <dir_info> [guard page]?
yes. That way both underflow and oveflow has a chance to be caught.
>
> And, why it is initializing the list chunk_info_list to 32 times and
> chunk_dir to 64 times, that is chunk_info_list[0...31] and
> chunk_dir[0...31][0...31]?
The second index of chunk_dir has size MALLOC_CHUNK_LISTS which is 4,
not 32.
More than one list of free chunk pages per chunk size is maintained to
allow for more randomization.
-Otto
>
> Could someone please provide some hints on the above queries?
>
> Regards,
> Neeraj
>