diff --git a/asan_example.cpp b/asan_example.cpp index 8abc67f..776bf56 100644 --- a/asan_example.cpp +++ b/asan_example.cpp @@ -1,10 +1,9 @@ -#define WIN32_MEAN_AND_LEAN -#include +#include #include #include #include -#include +#include #define IsPowerOfTwo(value, pot) ((((size_t)value) & (((size_t)pot) - 1)) == 0) #define AsanAlignment 8 @@ -57,15 +56,81 @@ static void PrintPoisonMemoryRegion(int step, char const *array, size_t array_si int main() { - printf("Here we demonstrate that ASAN poison-ing will only poison the\n" - "byte region if the region meets an 8 byte boundary. It will only\n" - "poison bytes upto the 8 byte boundary, any bytes that straddle\n" - "the boundary that do not hit the next 8 byte boundary are not\n" - "poison-ed.\n\n"); + printf( + "# ASAN Manual Poisoning\n" + "\n" + "## TLDR\n" + "\n" + "`__asan_poison_memory_region(ptr, size)`\n" + "\n" + "Poisons the byte region `[ptr, AlignDown(ptr+size, 8))`\n" + "\n" + "`__asan_unpoison_memory_region(ptr, size)`\n" + "\n" + "Unpoisons the byte region `[AlignDown(ptr, 8), ptr+size)`\n" + "\n" + "Use the provided macros that are conditionally enabled if ASAN is\n" + "defined from ``.\n" + "\n" + "```\n" + "ASAN_POISON_MEMORY_REGION(addr, size)\n" + "ASAN_UNPOISON_MEMORY_REGION(addr, size)\n" + "```\n" + "\n" + "If in doubt, use `__asan_address_is_poisoned` to sanity check the\n" + "ranges requested to be un/poisoned to avoid potential gaps in\n" + "marked-up memory that may lead to undetected read/writes.\n" + "\n" + "## Overview\n" + "\n" + "ASAN provides a way to manually markup ranges of bytes to\n" + "prohibit or permit reads to those addresses. There's a short\n" + "foot-note in Google's " + "[AddressSanitizerManualPoisoning](https://github.com/google/" + "sanitizers/wiki/AddressSanitizerManualPoisoning)\n" + "documentation that states:\n" + "\n" + "```\n" + "If you have a custom allocation arena, the typical workflow would be\n" + "to poison the entire arena first, and then unpoison allocated chunks\n" + "of memory leaving poisoned redzones between them. The allocated\n" + "chunks should start with 8-aligned addresses.\n" + "```\n" + "\n" + "This repository runs some simple tests to clarify the behaviour of\n" + "the API on un/aligned addresses at various sizes without having\n" + "to dig into source code or read the [ASAN paper](https://static." + "googleusercontent.com/media/research.google.com/en/pubs/archive/" + "37752.pdf).\n" + "\n" + "We use a stack-allocated 16 byte array and test un/poisoning\n" + "various ranges of bytes from different alignments to clarify the\n" + "poisoning behaviour of the API.\n" + "\n" + "This reveals that calling the API haphazardly, unaligned or\n" + "straddling boundaries can lead to gaps in poisoned memory and hide\n" + "potential leaks (as also demonstrated in [Manual ASAN poisoning and\n" + "alignment](https://github.com/mcgov/asan_alignment_example)).\n" + "\n" + "## References\n" + "\n" + "- [Manual ASAN poisoning and alignment](https://github.com/mcgov/asan_alignment_example) example by `mcgov`\n" + "- [Address Sanitizer: A Fast Address Sanity Checker](https://static.googleusercontent.com/media/research.google.com/en/pubs/archive/37752.pdf)\n" + "- [sanitizer/asan_interface.h](https://github.com/llvm-mirror/compiler-rt/blob/master/include/sanitizer/asan_interface.h)\n" + "\n" + "## Raw Test Results\n" + "Here we demonstrate that ASAN poison-ing will only poison the\n" + "byte region if the region meets an 8 byte boundary. It will only\n" + "poison bytes upto the 8 byte boundary, any bytes that straddle\n" + "the boundary that do not hit the next 8 byte boundary are not\n" + "poison-ed.\n" + "\n"); uint32_t const ASAN_ALIGNMENT = 8; uint32_t const REGION_WINDOW = 7; char array[16] = {}; + + printf("```\n"); for (size_t poison_index = 0; poison_index < sizeof(array) - (REGION_WINDOW - 1); poison_index++) { assert(IsPowerOfTwo(array, ASAN_ALIGNMENT)); @@ -87,12 +152,15 @@ int main() __asan_unpoison_memory_region(array, sizeof(array)); printf("\n"); } + printf("```\n"); printf( - "Now we demonstrate that unpoison-ing 1 byte in the 8 byte window\n" - "will unpoison all bytes prior to it up until the start of the window\n" - "until the previous 8 byte boundary.\n\n"); + "Now we demonstrate that unpoisoning 1 byte in the 8 byte window\n" + "will unpoison all bytes prior to it up until the previous 8 byte \n" + "boundary.\n" + "\n"); + printf("```\n"); for (size_t unpoison_index = 0; unpoison_index < sizeof(array); unpoison_index++) { assert(IsPowerOfTwo(array, ASAN_ALIGNMENT)); @@ -133,11 +201,14 @@ int main() __asan_unpoison_memory_region(array, sizeof(array)); } + printf("```\n"); printf( - "Unpoison-ing across 8 byte boundaries may lead to undesired\n" + "Unpoisoning across 8 byte boundaries may lead to undesired\n" "behaviour, with all bytes on the left side of the boundary being\n" "unpoisoned\n\n"); + + printf("```\n"); { // NOTE: Print byte array ================================================================== @@ -171,12 +242,6 @@ int main() PrintPoisonedBytes(4 /*step*/, array, sizeof(array)); } - - printf("\n\nTLDR:\n" - " __asan_poison_memory_region(ptr, size)\n" - " Poisons the byte region [ptr, AlignDown(ptr+size, 8))\n\n" - " __asan_unpoison_memory_region(ptr, size)\n" - " Unpoisons the byte region [AlignDown(ptr, 8), ptr+size)\n\n" - ); + printf("```\n"); return 0; } diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..3af6d42 --- /dev/null +++ b/readme.md @@ -0,0 +1,220 @@ +# ASAN Manual Poisoning + +## TLDR + +`__asan_poison_memory_region(ptr, size)` + +Poisons the byte region `[ptr, AlignDown(ptr+size, 8))` + +`__asan_unpoison_memory_region(ptr, size)` + +Unpoisons the byte region `[AlignDown(ptr, 8), ptr+size)` + +Use the provided macros that are conditionally enabled if ASAN is +defined from ``. + +``` +ASAN_POISON_MEMORY_REGION(addr, size) +ASAN_UNPOISON_MEMORY_REGION(addr, size) +``` + +If in doubt, use `__asan_address_is_poisoned` to sanity check the +ranges requested to be un/poisoned to avoid potential gaps in +marked-up memory that may lead to undetected read/writes. + +## Overview + +ASAN provides a way to manually markup ranges of bytes to +prohibit or permit reads to those addresses. There's a short +foot-note in Google's [AddressSanitizerManualPoisoning](https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning) +documentation that states: + +``` +If you have a custom allocation arena, the typical workflow would be +to poison the entire arena first, and then unpoison allocated chunks +of memory leaving poisoned redzones between them. The allocated +chunks should start with 8-aligned addresses. +``` + +This repository runs some simple tests to clarify the behaviour of +the API on un/aligned addresses at various sizes without having +to dig into source code or read the [ASAN paper](https://static.googleusercontent.com/media/research.google.com/en/pubs/archive/37752.pdf). + +We use a stack-allocated 16 byte array and test un/poisoning +various ranges of bytes from different alignments to clarify the +poisoning behaviour of the API. + +This reveals that calling the API haphazardly, unaligned or +straddling boundaries can lead to gaps in poisoned memory and hide +potential leaks (as also demonstrated in [Manual ASAN poisoning and +alignment](https://github.com/mcgov/asan_alignment_example) example +by `mcgov`. + +## References + +- [Manual ASAN poisoning and alignment](https://github.com/mcgov/asan_alignment_example) example by `mcgov` +- [Address Sanitizer: A Fast Address Sanity Checker](https://static.googleusercontent.com/media/research.google.com/en/pubs/archive/37752.pdf) +- [sanitizer/asan_interface.h](https://github.com/llvm-mirror/compiler-rt/blob/master/include/sanitizer/asan_interface.h) + +## Raw Test Results +Here we demonstrate that ASAN poison-ing will only poison the +byte region if the region meets an 8 byte boundary. It will only +poison bytes upto the 8 byte boundary, any bytes that straddle +the boundary that do not hit the next 8 byte boundary are not +poison-ed. + +``` + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x | +2. __asan_address_is_poisoned | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x | +2. __asan_address_is_poisoned x x x x x x x | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x | x +2. __asan_address_is_poisoned x x x x x x | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x | x x +2. __asan_address_is_poisoned x x x x x | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x | x x x +2. __asan_address_is_poisoned x x x x | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x | x x x x +2. __asan_address_is_poisoned x x x | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x | x x x x x +2. __asan_address_is_poisoned x x | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x | x x x x x x +2. __asan_address_is_poisoned x | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region | x x x x x x x +2. __asan_address_is_poisoned | + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region | x x x x x x x +2. __asan_address_is_poisoned | x x x x x x x + +``` +Now we demonstrate that unpoisoning 1 byte in the 8 byte window +will unpoison all bytes prior to it up until the previous 8 byte +boundary. + +``` + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | +4. __asan_address_is_poisoned x x x x x x x | x x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | +4. __asan_address_is_poisoned x x x x x x | x x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | +4. __asan_address_is_poisoned x x x x x | x x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | +4. __asan_address_is_poisoned x x x x | x x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | +4. __asan_address_is_poisoned x x x | x x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | +4. __asan_address_is_poisoned x x | x x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | +4. __asan_address_is_poisoned x | x x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | +4. __asan_address_is_poisoned | x x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region | x +4. __asan_address_is_poisoned x x x x x x x x | x x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region | x +4. __asan_address_is_poisoned x x x x x x x x | x x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region | x +4. __asan_address_is_poisoned x x x x x x x x | x x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region | x +4. __asan_address_is_poisoned x x x x x x x x | x x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region | x +4. __asan_address_is_poisoned x x x x x x x x | x x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region | x +4. __asan_address_is_poisoned x x x x x x x x | x x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region | x +4. __asan_address_is_poisoned x x x x x x x x | x + + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region | x +4. __asan_address_is_poisoned x x x x x x x x | + +``` +Unpoisoning across 8 byte boundaries may lead to undesired +behaviour, with all bytes on the left side of the boundary being +unpoisoned + +``` + Byte Array 00 01 02 03 04 05 06 07 | 08 09 10 11 12 13 14 15 +1. __asan_poison_memory_region x x x x x x x x | x x x x x x x x +2. __asan_address_is_poisoned x x x x x x x x | x x x x x x x x +3. __asan_unpoison_memory_region x | x +4. __asan_address_is_poisoned | x x x x x x x +```