├── .clang-format ├── .gitignore ├── LICENSE ├── main.c ├── makefile ├── readme.md ├── smart.c ├── smart.h ├── test.c └── trampoline.S /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | AlignAfterOpenBracket: Align 3 | AlignConsecutiveAssignments: 'false' 4 | AlignConsecutiveDeclarations: 'false' 5 | AlignEscapedNewlinesLeft: 'true' 6 | AllowAllParametersOfDeclarationOnNextLine: 'true' 7 | AllowShortBlocksOnASingleLine: 'false' 8 | AllowShortCaseLabelsOnASingleLine: 'false' 9 | AllowShortFunctionsOnASingleLine: 'None' 10 | AllowShortIfStatementsOnASingleLine: 'true' 11 | AllowShortLoopsOnASingleLine: 'true' 12 | BinPackArguments: 'false' 13 | BinPackParameters: 'false' 14 | BreakBeforeBraces: Attach 15 | ColumnLimit: '72' 16 | ContinuationIndentWidth: '3' 17 | IndentCaseLabels: 'false' 18 | IndentWidth: '3' 19 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 20 | Language: Cpp 21 | MaxEmptyLinesToKeep: '2' 22 | SortIncludes: 'true' 23 | SpaceAfterCStyleCast: 'true' 24 | SpaceBeforeAssignmentOperators: 'true' 25 | SpaceBeforeParens: Always 26 | SpaceInEmptyParentheses: 'false' 27 | SpacesInAngles: 'false' 28 | SpacesInCStyleCastParentheses: 'false' 29 | SpacesInContainerLiterals: 'true' 30 | SpacesInParentheses: 'false' 31 | SpacesInSquareBrackets: 'false' 32 | Standard: Cpp11 33 | TabWidth: '3' 34 | UseTab: Never 35 | 36 | ... 37 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | smart 3 | test 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kevin Albertson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include "smart.h" 2 | 3 | #include 4 | #include 5 | 6 | void f () { 7 | char *data = free_on_exit (malloc (sizeof (char))); 8 | } 9 | 10 | int main () { 11 | printf ("run me with valgrind\n"); 12 | f (); 13 | } -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # compile on 32 bit architecture without compiler optimizations 2 | all: test smart 3 | 4 | test: test.c smart.c trampoline.S 5 | gcc -o test -O0 -m32 test.c smart.c trampoline.S 6 | 7 | smart: main.c smart.c trampoline.S 8 | gcc -o smart -O0 -m32 main.c smart.c trampoline.S 9 | 10 | format: main.c smart.c test.c smart.h 11 | clang-format -style=file -i main.c smart.c test.c smart.h 12 | 13 | clean: 14 | rm smart test 15 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | Stupid Smart Pointers 2 | --------------------- 3 | 4 | A proof-of-concept smart pointer library in C. 5 | 6 | This code is non-portable, and was tested on x86 Ubuntu 16.04 with gcc 5.4.0. It is intended to be as simple as possible. 7 | 8 | See [the article](http://blog.kevinalbs.com/stupid_smart_pointers) for an explanation of how it works. 9 | -------------------------------------------------------------------------------- /smart.c: -------------------------------------------------------------------------------- 1 | #include "smart.h" 2 | 3 | #include // free 4 | #include // memset 5 | 6 | /* these limits are arbitrary. */ 7 | #define STACK_SIZE 256 8 | #define MAX_PER_FRAME 32 9 | 10 | typedef struct { 11 | int caller_ebp; /* ebp of the caller. This identifes the frame. */ 12 | int caller_eip; /* the original return eip of the caller. */ 13 | void *tracked_pointers[MAX_PER_FRAME]; 14 | int tail; /* points to one past last entry. */ 15 | } tracked_stack_entry_t; 16 | 17 | typedef struct { 18 | tracked_stack_entry_t stack[STACK_SIZE]; 19 | int tail; /* points to one past last entry. */ 20 | } tracked_stack_t; 21 | 22 | /* forward declare the assembly trampoline. */ 23 | void trampoline (); 24 | 25 | tracked_stack_t tracked = {0}; 26 | 27 | int do_free () { 28 | tracked_stack_entry_t *entry = tracked.stack + (tracked.tail - 1); 29 | tracked.tail--; /* pop. */ 30 | for (int i = 0; i < MAX_PER_FRAME; i++) { 31 | if (entry->tracked_pointers[i] == 0) break; 32 | free (entry->tracked_pointers[i]); 33 | } 34 | return entry->caller_eip; 35 | } 36 | 37 | void *free_on_exit (void *entry) { 38 | int ret_addr = 0; 39 | int do_free_addr = (int) &do_free; 40 | int *caller_ebp; 41 | 42 | /* get the value of ebp. */ 43 | __asm__("movl (%%ebp), %0 \n" 44 | : "=r"(caller_ebp) /* output. */ 45 | ); 46 | 47 | /* check if there is a pre-existing stack entry for this caller 48 | * (identified by caller's ebp). */ 49 | tracked_stack_entry_t *tracked_entry; 50 | 51 | if (tracked.tail > 0 && 52 | tracked.stack[tracked.tail - 1].caller_ebp == (int) caller_ebp) { 53 | /* re-use. */ 54 | tracked_entry = tracked.stack + tracked.tail - 1; 55 | } else { 56 | /* make a new entry. */ 57 | tracked_entry = tracked.stack + tracked.tail++; 58 | memset (tracked_entry, 0, sizeof (*tracked_entry)); 59 | tracked_entry->caller_ebp = (int) caller_ebp; 60 | /* hijack caller's return eip to return to do_free. */ 61 | tracked_entry->caller_eip = *(caller_ebp + 1); 62 | *(caller_ebp + 1) = (int) trampoline; 63 | } 64 | 65 | /* push the passed pointer. */ 66 | tracked_entry->tracked_pointers[tracked_entry->tail++] = entry; 67 | return entry; 68 | } 69 | -------------------------------------------------------------------------------- /smart.h: -------------------------------------------------------------------------------- 1 | #ifndef _SMART 2 | #define _SMART 3 | void *free_on_exit (void *); 4 | #endif -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "smart.h" 4 | 5 | static void test_single () { 6 | char *x = (char *) free_on_exit (malloc (sizeof (char))); 7 | } 8 | 9 | static void test_multiple () { 10 | char *x[] = {0, 0, 0}; 11 | x[0] = (char *) free_on_exit (malloc (sizeof (char))); 12 | x[1] = (char *) free_on_exit (malloc (sizeof (char))); 13 | x[2] = (char *) free_on_exit (malloc (sizeof (char))); 14 | } 15 | 16 | static void test_nested () { 17 | char *x = (char *) free_on_exit (malloc (sizeof (char))); 18 | test_single (); 19 | test_multiple (); 20 | } 21 | 22 | /* Run this with valgrind. You may need to install 32 bit debug 23 | * symbols for the C library. On Ubuntu this is libc6-dbg:i386. 24 | * See https://stackoverflow.com/q/10172302/774658 */ 25 | int main () { 26 | test_single (); 27 | test_multiple (); 28 | test_nested (); 29 | } -------------------------------------------------------------------------------- /trampoline.S: -------------------------------------------------------------------------------- 1 | # This can be compiled by itself with `as --32` 2 | # This is GNU assembler syntax (aka AT-T syntax) (src, dest) 3 | .section .text 4 | .globl trampoline 5 | .type trampoline, @function 6 | trampoline: 7 | call do_free 8 | jmp %eax # jump directly back to the old eip. 9 | --------------------------------------------------------------------------------