Week 0x9 II
Crypto
malloc
free
Python has no array (NumPy does)
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } ;
arr = { 1, 2, 3 } ; // compile error
arr[15] = -1 ; // runtime error - "stack smashing"`| Pointers | Arrays |
|---|---|
Fixed size, like 8 |
Any specified size |
Change with = |
= triggers error |
| Names some bits | Provides/names bits |
| Can describe any array | One specific array |
mallocvoid * is new, that is how we refer to something but we don’t know to what.
void *
size is the number of bytesvoid *, we can use a cast to change it to some other star.
size_tmalloc(0xFF...) will either crash or return a void * of zerovoid *, the largest of which would be 0xFF…void * and size_t are the same size.void *#include <stdlib.h> /* for size_t */
#include <assert.h> // for assert
int main() {
assert(sizeof(void *) == sizeof(size_t)) ; /* pass */
assert(sizeof(char) == 1) ; /* pass */
assert(1 == 0) ; /* fail */
return 0;
}char arr0[256], arr1[256], arr2[256];
char *ptr0 = malloc(256), *ptr1 = malloc(256), *ptr2 = malloc(256);
printf("%p\n%p\n%p\n%p\n%p\n%p\n", arr0, arr1, arr2, ptr0, ptr1, ptr2) ;| Stack | Heap |
|---|---|
| Fixed Size | Arbitrary Size |
| Holds Function Variables | Returned by a function (malloc) |
| Defined when compiling by GCC | Defined when running by OS using magic |
Higher/larger (~0xFF...) |
Lower/smaller (~0x00...) |
| Jenny Chen | Ruohao Guo |
|---|---|
| she/her | she/her |
| Software Engineer | Graduate Research Assistant |
| Apple | Georgia Institute of Technology |
| B.S. Computer Science, 2021, UIUC | B.S. Computer Science, 2021, UIUC |


sizeof(int) bytes for variable a for function main

a for mainsizeof(int) bytes for variable b for function main
(int)-3 in these bytes.

a for mainb for mainsizeof(int) bytes for variable c for main
(int)12345

a for mainb for mainc for mainsizeof(int) bytes for variable a for hello
(int)100hello.a vs main.a

a for mainb for mainc for main
(int)12345

returna already at the top (bottom) of the stack.
100, no longer hello.a

returnd100 never moves.

main.amain.b=3main.c=12345hello
hello.a=100returna’s value into into main.d



sizeof(int *) bytes for main.p.sizeof(int) bytesp.
*p is the value of the bits on the heap.
intp is the value of the bits on the stack.
int * or void *

0 at main.p
0 at the location described by main.p

main.q2 * sizeof(int) bytesintsmain.q.

p0 at main.pq

p0 at main.pq1 at main.q

p0 at main.pq1 at main.q2 at index 1 of the int array which begins at main.q

p0 at main.pq1 at main.q2 in main.q[1]main.q (a location) in main.p

p0 at main.pq1 at main.q2 in main.q[1]main.q (a location) in main.p

malloc
free
0.1.
{1, 2} in this case.
p is a pointer returned from malloc
malloc in your code should have a corresponding freep” forgotten!1 you left in “old p” forever!free(p) and the bytes return to circulation.0 persists until overwritten*valgrind
valgrind$ gcc leaky.c
$ valgrind ./a.out
==1331== Memcheck, a memory error detector
==1331== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==1331== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==1331== Command: ./a.out
==1331==
==1331==
==1331== HEAP SUMMARY:
==1331== in use at exit: 8 bytes in 2 blocks
==1331== total heap usage: 2 allocs, 0 frees, 8 bytes allocated
==1331==
==1331== LEAK SUMMARY:
==1331== definitely lost: 4 bytes in 1 blocks
==1331== indirectly lost: 0 bytes in 0 blocks
==1331== possibly lost: 0 bytes in 0 blocks
==1331== still reachable: 4 bytes in 1 blocks
==1331== suppressed: 0 bytes in 0 blocks
==1331== Rerun with --leak-check=full to see details of leaked memory
==1331==
==1331== For lists of detected and suppressed errors, rerun with: -s
==1331== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)leaky.c
We can generate a silly outcome at high probability by:
#include <stdio.h>
#include <stdlib.h>
void main() {
int *p = malloc(sizeof(int)), *q, i ;
*p = 1 ;
printf("%d\n", *p) ;
free(p) ;
for ( i = 0 ; i < 1000000 ; i++) {
q = malloc(0xFF) ;
}
printf("%d\n", *p) ;
}#include <stdio.h>
#include <stdlib.h>
void main() {
int *p = malloc(sizeof(int)), *q, i ;
*p = 1 ;
printf("%d\n", *p) ;
for ( i = 0 ; i < 1000000 ; i++) {
q = malloc(0xFF) ;
}
printf("%d\n", *p) ;
}#include <stdio.h>
#include <stdlib.h>
void main() {
int *p = malloc(sizeof(int)), *q, i ;
*p = 1 ;
printf("%d\n", *p) ;
free(p)
printf("%d\n", *p) ;
}1 is unprotected but not yet overwritten.malloc
free
| Type | Use | Print code | sizeof(), usually |
|---|---|---|---|
void * |
a memory location | %p |
8 |
size_t |
size of some memory | %zu or %ld |
8 |
char buf[8] |
8 values of size 1 | %s |
8 |
long,long int,int64_t |
\(\text{abs}(x) <= 2^{63}\) | %ld |
8 |
gcc warnings/errors:#include <stdio.h>
#include <stdlib.h>
void main() {
char *buf[8];
void *p = (void *)buf;
void *q = malloc(1);
size_t dist = (size_t)p - (size_t)q;
printf("q was malloc'ed %zu bytes from stack allocated p.\n", dist);
}$ cat leaky.c
void main() {
char buf[2] = "h";
void *letter = buf;
void *ptr = 'h';
}
$ gcc leaky.c
leaky.c: In function ‘main’:
leaky.c:7:25: warning: initialization of ‘void *’ from ‘int’ makes pointer from integer without a cast [-Wint-conversio]
7 | void *ptr = 'h';
|
$ python3 -c 'print(ord("h"))'
104 void * is fine - both addressesint main() {
char *ptr = malloc(8) ; // error-prone, ambigious
char *str = (char *)malloc(sizeof(char) * 8) ; // more intentional
}sizeof(int) != 1.q is must be some value other than 1 away from q[1]q[1*sizeof(int)]
>>> x, y, s, t = 1, 2, "h", "i"
>>> x + y 3
>>> x + s
Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for +: 'int' and 'str'
>>> s + t 'hi'
>>>gcc’sgcc stops you.gcc.leaky.c: In function ‘main’:
leaky.c:2:13: error: invalid operands to binary + (have ‘char *’ and ‘char *’)
2 | "a" + "b";
| ~~~ ^
| | |
| | char *
| char *gccprintf("%p + %p = %p \n", (void *)p, (void *)x, (void *)((char *)p + x)) ;
printf("%p + %p = %p \n", (void *)p, (void *)x, (void *)((int *)p + x)) ;
printf("%p + %p = %p \n", (void *)p, (void *)x, (void *)((long *)p + x)) ;`[]int arr[4] = { 0x10, 0x100, 0x1000, 0x10000 } ;
printf(" arr+1 : %p\n", arr+1) ;
printf("*(arr+1): %p\n", *(arr+1)) ;
printf("(*arr+1): %p\n", (*arr+1)) ;
printf(" arr[1]: %p\n", arr[1]) ;&& is inverse *int main() {
int x = 0xF0, y = 0x0F, *p; // just unique vals
p = &y;
printf("*p = %x, p = %p\n", *p, p);
printf(" y = %x, &y = %p\n", y, &y);
}p = &y \(\implies\) *p = y* is not (quite) inverse &int main() {
int x = 0xF0, y = 0x0F, *p; // just unique vals
*p = y;
printf("*p = %x, p = %p\n", *p, p);
printf(" y = %x, &y = %p\n", y, &y);
}*p = y \(\not\!\!\!\implies\) p = &yThere is no guarantee malloc worked
malloc(∞)malloc
free