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 |
malloc
void *
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_t
malloc(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 main
sizeof(int)
bytes for variable b
for function main
(int)-3
in these bytes.
a
for main
b
for main
sizeof(int)
bytes for variable c
for main
(int)12345
a
for main
b
for main
c
for main
sizeof(int)
bytes for variable a
for hello
(int)100
hello.a
vs main.a
a
for main
b
for main
c
for main
(int)12345
return
a
already at the top (bottom) of the stack.
100
, no longer hello.a
return
d
100
never moves.
main.a
main.b=3
main.c=12345
hello
hello.a=100
return
a
’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.
int
p
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.q
2 * sizeof(int)
bytesint
smain.q
.
p
0
at main.p
q
p
0
at main.p
q
1
at main.q
p
0
at main.p
q
1
at main.q
2
at index 1
of the int
array which begins at main.q
p
0
at main.p
q
1
at main.q
2
in main.q[1]
main.q
(a location) in main.p
p
0
at main.p
q
1
at main.q
2
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 free
p
” 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 *
gcc
printf("%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 = &y
There is no guarantee malloc
worked
malloc(∞)
malloc
free