1
0

CanaryAllocator.c 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. /* vim: set expandtab ts=4 sw=4: */
  2. /*
  3. * You may redistribute this program and/or modify it under the terms of
  4. * the GNU General Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. *
  7. * This program is distributed in the hope that it will be useful,
  8. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. * GNU General Public License for more details.
  11. *
  12. * You should have received a copy of the GNU General Public License
  13. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #include "memory/Allocator.h"
  16. #include "memory/CanaryAllocator.h"
  17. #include "memory/CanaryAllocator_pvt.h"
  18. #include "util/Assert.h"
  19. #include "util/Bits.h"
  20. #include "util/Identity.h"
  21. #include <stdint.h>
  22. #define SIZE_INTS(size) ((size + 11) / 4)
  23. #define SIZE_BYTES(size) (SIZE_INTS(size) * 4)
  24. int CanaryAllocator_isOverflow(struct CanaryAllocator_pvt* ctx)
  25. {
  26. if (ctx->allocCount <= 0) {
  27. return 0;
  28. }
  29. if (ctx->firstFile != ctx->allocations[0].file) {
  30. fprintf(stderr, "CanaryAllocator:%d Allocations list is corrupt\n", __LINE__);
  31. return 1;
  32. }
  33. for (int i = 0; i < ctx->allocCount; i++) {
  34. if (ctx->allocations[i].canaryValue != ctx->canaryValue) {
  35. fprintf(stderr, "CanaryAllocator:%d Allocations list is corrupt\n", __LINE__);
  36. return 1;
  37. }
  38. if (*ctx->allocations[i].beginCanary != ctx->canaryValue
  39. || *ctx->allocations[i].endCanary != ctx->canaryValue)
  40. {
  41. fprintf(stderr, "%s:%d Buffer overflow, %s canary corrupt\n",
  42. ctx->allocations[i].file, ctx->allocations[i].line,
  43. (*ctx->allocations[i].beginCanary != ctx->canaryValue) ? "begin" : "end");
  44. return 1;
  45. }
  46. }
  47. return 0;
  48. }
  49. /** @see Allocator->free() */
  50. static void freeAllocator(const struct Allocator* allocator, const char* identFile, int identLine)
  51. {
  52. struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
  53. CanaryAllocator_check(allocator);
  54. ctx->alloc->free(ctx->alloc, identFile, identLine);
  55. }
  56. static inline void* newAllocation(struct CanaryAllocator_pvt* ctx,
  57. uint32_t* allocation,
  58. size_t sizeInts,
  59. const char* identFile,
  60. int identLine)
  61. {
  62. Assert_always(!CanaryAllocator_isOverflow(ctx));
  63. ctx->allocCount++;
  64. ctx->allocations =
  65. Allocator_realloc(ctx->alloc,
  66. ctx->allocations,
  67. ctx->allocCount * sizeof(struct CanaryAllocator_Allocation));
  68. if (ctx->allocCount == 1) {
  69. // This is used to check the integrity of the first element in the list.
  70. ctx->firstFile = identFile;
  71. }
  72. struct CanaryAllocator_Allocation* a = &ctx->allocations[ctx->allocCount - 1];
  73. a->canaryValue = ctx->canaryValue;
  74. a->file = identFile;
  75. a->line = identLine;
  76. a->beginCanary = allocation;
  77. a->endCanary = &allocation[sizeInts - 1];
  78. allocation[0] = ctx->canaryValue;
  79. allocation[sizeInts - 1] = ctx->canaryValue;
  80. return (void*) (&allocation[1]);
  81. }
  82. /** @see Allocator->malloc() */
  83. static void* allocatorMalloc(size_t size,
  84. const struct Allocator* allocator,
  85. const char* identFile,
  86. int identLine)
  87. {
  88. struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
  89. // get it on an even number of ints.
  90. uint32_t* out = ctx->alloc->malloc(SIZE_BYTES(size), ctx->alloc, identFile, identLine);
  91. return newAllocation(ctx, out, SIZE_INTS(size), identFile, identLine);
  92. }
  93. /** @see Allocator->calloc() */
  94. static void* allocatorCalloc(size_t length,
  95. size_t count,
  96. const struct Allocator* allocator,
  97. const char* identFile,
  98. int identLine)
  99. {
  100. void* pointer = allocatorMalloc(length * count, allocator, identFile, identLine);
  101. Bits_memset(pointer, 0, length * count);
  102. return pointer;
  103. }
  104. /** @see Allocator->clone() */
  105. static void* allocatorClone(size_t length,
  106. const struct Allocator* allocator,
  107. const void* toClone,
  108. const char* identFile,
  109. int identLine)
  110. {
  111. void* pointer = allocatorMalloc(length, allocator, identFile, identLine);
  112. Bits_memcpy(pointer, toClone, length);
  113. return pointer;
  114. }
  115. /** @see Allocator->onFree() */
  116. static void* addOnFreeJob(void (* callback)(void* callbackContext),
  117. void* callbackContext,
  118. const struct Allocator* allocator)
  119. {
  120. struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
  121. return ctx->alloc->onFree(callback, callbackContext, ctx->alloc);
  122. }
  123. static bool removeOnFreeJob(void* toRemove, struct Allocator* alloc)
  124. {
  125. struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) alloc);
  126. return ctx->alloc->notOnFree(toRemove, ctx->alloc);
  127. }
  128. /** @see Allocator->realloc() */
  129. static void* allocatorRealloc(const void* original,
  130. size_t size,
  131. const struct Allocator* allocator,
  132. const char* identFile,
  133. int identLine)
  134. {
  135. struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
  136. if (((uint8_t*) original) == NULL) {
  137. return allocatorMalloc(size, allocator, identFile, identLine);
  138. }
  139. uint32_t* beginCanary = ((uint32_t*) original) - 1;
  140. Assert_always(*beginCanary == ctx->canaryValue);
  141. for (int i = 0; i < ctx->allocCount; i++) {
  142. if (ctx->allocations[i].beginCanary == beginCanary) {
  143. Bits_memmove(&ctx->allocations[i],
  144. &ctx->allocations[i + 1],
  145. (ctx->allocCount - i - 1) * sizeof(struct CanaryAllocator_Allocation));
  146. ctx->allocCount--;
  147. uint32_t* out = ctx->alloc->realloc(
  148. ((uint32_t*)original) - 1, SIZE_BYTES(size), ctx->alloc, identFile, identLine);
  149. return newAllocation(ctx, out, SIZE_INTS(size), identFile, identLine);
  150. }
  151. }
  152. Assert_true(0);
  153. }
  154. /** @see Allocator_child() */
  155. static struct Allocator* childAllocator(const struct Allocator* allocator,
  156. const char* identFile,
  157. int identLine)
  158. {
  159. struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
  160. return CanaryAllocator_new(ctx->alloc->child(ctx->alloc, identFile, identLine), ctx->rand);
  161. }
  162. /** @see MallocAllocator.h */
  163. struct Allocator* CanaryAllocator_new(struct Allocator* alloc, struct Random* rand)
  164. {
  165. struct CanaryAllocator_pvt* ctx = Allocator_clone(alloc, (&(struct CanaryAllocator_pvt) {
  166. .pub = {
  167. .free = freeAllocator,
  168. .malloc = allocatorMalloc,
  169. .calloc = allocatorCalloc,
  170. .clone = allocatorClone,
  171. .realloc = allocatorRealloc,
  172. .child = childAllocator,
  173. .onFree = addOnFreeJob,
  174. .notOnFree = removeOnFreeJob,
  175. .context = alloc->context
  176. },
  177. .alloc = alloc,
  178. .rand = rand,
  179. .canaryValue = 0xdc57b1a4
  180. }));
  181. Identity_set(ctx);
  182. if (rand) {
  183. ctx->canaryValue = Random_uint32(rand);
  184. }
  185. return &ctx->pub;
  186. }
  187. void CanaryAllocator_check(const struct Allocator* allocator)
  188. {
  189. struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
  190. Assert_always(!CanaryAllocator_isOverflow(ctx));
  191. }