The most common way is that it stores some information immediately before the address it returns to you. So if malloc returns the address 0x1004, internally, malloc will have put aside the memory started at 0x0FFC and will store various information such as the size somewhere in the memory between 0xFFC - 0x1003 but the application will be told the allocation starts at 0x1004.
The only thing that matters to free is getting the exact same address as what malloc returned.