Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Added a new allocator which places canaries on each side of every all…

…ocation to detect buffer overflow.
  • Loading branch information...
commit f23750d347179e3f59e6a302fca8fe77cb02c328 1 parent 461e705
Caleb James DeLisle authored
View
3  memory/BufferAllocator.h
@@ -15,7 +15,8 @@
#ifndef BufferAllocator_H
#define BufferAllocator_H
-#include "Allocator.h"
+#include "memory/Allocator.h"
+#include "util/UniqueName.h"
/**
* Create a new Allocator which allocates from to a user supplied buffer.
View
1  memory/CMakeLists.txt
@@ -16,6 +16,7 @@ cmake_minimum_required(VERSION 2.4)
add_library(cjdmemory
BufferAllocator.c
MallocAllocator.c
+ CanaryAllocator.c
)
# Everything must be tested.
View
152 memory/CanaryAllocator.c
@@ -0,0 +1,152 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "memory/Allocator.h"
+#include "memory/CanaryAllocator.h"
+#include "memory/CanaryAllocator_pvt.h"
+#include "util/Assert.h"
+#include "util/Bits.h"
+#include "util/Identity.h"
+
+#include <stdint.h>
+
+#define SIZE_INTS(size) ((size + 11) / 4)
+#define SIZE_BYTES(size) (SIZE_INTS(size) * 4)
+
+int CanaryAllocator_isOverflow(struct CanaryAllocator_pvt* ctx)
+{
+ if (ctx->canaryCount > 0) {
+ if (ctx->firstCanary != ctx->canaries[0]
+ || ctx->lastCanary != ctx->canaries[ctx->canaryCount - 1])
+ {
+ return 1;
+ }
+ }
+ for (int i = 0; i < ctx->canaryCount; i++) {
+ if (*ctx->canaries[i] != ctx->canaryValue) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/** @see Allocator->free() */
+static void freeAllocator(const struct Allocator* allocator)
+{
+ struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
+ Assert_always(!CanaryAllocator_isOverflow(ctx));
+ ctx->alloc->free(ctx->alloc);
+}
+
+static inline void* newAllocation(struct CanaryAllocator_pvt* ctx,
+ uint32_t* allocation,
+ size_t sizeInts)
+{
+ ctx->canaries =
+ ctx->alloc->realloc(ctx->canaries, (ctx->canaryCount + 2) * sizeof(uint32_t*), ctx->alloc);
+ allocation[0] = ctx->canaryValue;
+ if (ctx->canaryCount == 0) {
+ ctx->firstCanary = allocation;
+ }
+ ctx->canaries[ctx->canaryCount++] = allocation;
+ allocation[sizeInts - 1] = ctx->canaryValue;
+ ctx->canaries[ctx->canaryCount++] = &allocation[sizeInts - 1];
+ ctx->lastCanary = &allocation[sizeInts - 1];
+ return (void*) (&allocation[1]);
+}
+
+/** @see Allocator->malloc() */
+static void* allocatorMalloc(size_t size, const struct Allocator* allocator)
+{
+ struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
+ // get it on an even number of ints.
+ uint32_t* out = ctx->alloc->malloc(SIZE_BYTES(size), ctx->alloc);
+ return newAllocation(ctx, out, SIZE_INTS(size));
+}
+
+/** @see Allocator->calloc() */
+static void* allocatorCalloc(size_t length, size_t count, const struct Allocator* allocator)
+{
+ void* pointer = allocatorMalloc(length * count, allocator);
+ Bits_memset(pointer, 0, length * count);
+ return pointer;
+}
+
+/** @see Allocator->clone() */
+static void* allocatorClone(size_t length, const struct Allocator* allocator, const void* toClone)
+{
+ void* pointer = allocatorMalloc(length, allocator);
+ Bits_memcpy(pointer, toClone, length);
+ return pointer;
+}
+
+/** @see Allocator->realloc() */
+static void* allocatorRealloc(const void* original,
+ size_t size,
+ const struct Allocator* allocator)
+{
+ struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
+ uint32_t* beginCanary = ((uint32_t*) original) - 1;
+ Assert_always(*beginCanary == ctx->canaryValue);
+ for (int i = 0; i < ctx->canaryCount; i++) {
+ if (ctx->canaries[i] == beginCanary) {
+ Assert_always(ctx->canaryCount > i + 1 && *ctx->canaries[i + 1] == ctx->canaryValue);
+ for (int j = i + 2; j < ctx->canaryCount; j++) {
+ ctx->canaries[j - 2] = ctx->canaries[j];
+ }
+ ctx->canaryCount -= 2;
+ break;
+ }
+ }
+ uint32_t* out = ctx->alloc->realloc(((uint32_t*)original) - 1, SIZE_BYTES(size), ctx->alloc);
+ return newAllocation(ctx, out, SIZE_INTS(size));
+}
+
+/** @see Allocator->child() */
+static struct Allocator* childAllocator(const struct Allocator* allocator)
+{
+ struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
+ return CanaryAllocator_new(ctx->alloc->child(ctx->alloc), ctx->rand);
+}
+
+/** @see MallocAllocator.h */
+struct Allocator* CanaryAllocator_new(struct Allocator* alloc, struct Random* rand)
+{
+ struct CanaryAllocator_pvt* ctx = Allocator_clone(alloc, (&(struct CanaryAllocator_pvt) {
+ .pub = {
+ .free = freeAllocator,
+ .malloc = allocatorMalloc,
+ .calloc = allocatorCalloc,
+ .clone = allocatorClone,
+ .realloc = allocatorRealloc,
+ .child = childAllocator,
+ .onFree = alloc->onFree,
+ .notOnFree = alloc->notOnFree
+ },
+ .alloc = alloc,
+ .rand = rand,
+ .canaryValue = 0xdc57b1a4
+ }));
+ Identity_set(ctx);
+ if (rand) {
+ ctx->canaryValue = Random_uint32(rand);
+ }
+ return &ctx->pub;
+}
+
+void CanaryAllocator_check(struct Allocator* allocator)
+{
+ struct CanaryAllocator_pvt* ctx = Identity_cast((struct CanaryAllocator_pvt*) allocator);
+ Assert_always(!CanaryAllocator_isOverflow(ctx));
+}
View
41 memory/CanaryAllocator.h
@@ -0,0 +1,41 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef CanaryAllocator_H
+#define CanaryAllocator_H
+
+#include "crypto/Random.h"
+#include "memory/Allocator.h"
+
+/**
+ * Wrap an existing allocator with one which places canaries on either side of the allocations.
+ * Canaries are checked when you call CanaryAllocator_check(), allocator->realloc(), or
+ * allocator->free().
+ * If either of the canary numbers has been changed (by a buffer overflow) then the program is
+ * aborted. All children of a CanaryAllocator are also CanaryAllocators
+ *
+ * @param allocator allocator to wrap.
+ * @param rand a source of canary numbers.
+ * @return a new CanaryAllocator
+ */
+struct Allocator* CanaryAllocator_new(struct Allocator* alloc, struct Random* rand);
+
+/**
+ * Abort if any of the canaries have been written over.
+ *
+ * @param allocator the canary allocator.
+ */
+void CanaryAllocator_check(struct Allocator* allocator);
+
+#endif
View
43 memory/CanaryAllocator_pvt.h
@@ -0,0 +1,43 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#ifndef CanaryAllocator_pvt_H
+#define CanaryAllocator_pvt_H
+
+#include "crypto/Random.h"
+#include "memory/Allocator.h"
+#include "memory/CanaryAllocator.h"
+#include "util/Identity.h"
+
+#include <stdint.h>
+
+struct CanaryAllocator_pvt
+{
+ struct Allocator pub;
+ struct Allocator* alloc;
+ uint32_t** canaries;
+
+ /** Store the first and last canary pointers to make sure the list hasn't been corrupted. */
+ uint32_t* firstCanary;
+ uint32_t* lastCanary;
+
+ int canaryCount;
+ uint32_t canaryValue;
+ struct Random* rand;
+ Identity
+};
+
+int CanaryAllocator_isOverflow(struct CanaryAllocator_pvt* ctx);
+
+#endif
View
4 memory/test/CMakeLists.txt
@@ -14,7 +14,7 @@ cmake_minimum_required(VERSION 2.4)
string(REGEX REPLACE "^.*/" "" main_dir_name ${CMAKE_SOURCE_DIR})
string(REPLACE ${CMAKE_SOURCE_DIR} ${main_dir_name} this_dir ${CMAKE_CURRENT_SOURCE_DIR})
message("-- Tests to run for " ${this_dir})
-remove_definitions(-Werror)
+#remove_definitions(-Werror)
add_definitions(-DTEST)
# Anything in this dir which ends with "test.c" is considered a test.
@@ -24,7 +24,7 @@ foreach(test_source_fullpath ${tests})
string(REPLACE "test.c" "test" test_bin ${test_source})
message(" " ${test_source})
add_executable(${test_bin} ${test_source})
- target_link_libraries(${test_bin} cjdmemory)
+ target_link_libraries(${test_bin} cjdmemory cjdns-crypto-random)
add_test(${test_bin} ${test_bin})
endforeach()
# Add an empty line after tests.
View
88 memory/test/CanaryAllocator_test.c
@@ -0,0 +1,88 @@
+/* vim: set expandtab ts=4 sw=4: */
+/*
+ * You may redistribute this program and/or modify it under the terms of
+ * the GNU General Public License as published by the Free Software Foundation,
+ * either version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "memory/Allocator.h"
+#include "memory/BufferAllocator.h"
+#include "memory/CanaryAllocator_pvt.h"
+#include "util/Assert.h"
+
+void tryOverflowLow(struct Allocator* alloc, int bytes)
+{
+ uint8_t* buff = Allocator_malloc(alloc, bytes);
+ buff[-1] = 0;
+ Assert_always(CanaryAllocator_isOverflow((struct CanaryAllocator_pvt*) alloc));
+}
+
+void tryOverflowHigh(struct Allocator* alloc, int bytes)
+{
+ uint8_t* buff = Allocator_malloc(alloc, bytes);
+ buff[bytes + 3] = 0;
+ Assert_always(CanaryAllocator_isOverflow((struct CanaryAllocator_pvt*) alloc));
+}
+
+void tryOverflow(struct Allocator* alloc, int bytes)
+{
+ // can't free these child allocators or they will explode!
+ struct Allocator* child = Allocator_child(alloc);
+ tryOverflowLow(child, bytes);
+ child = Allocator_child(alloc);
+ tryOverflowHigh(alloc, bytes);
+}
+
+void tryCalloc(struct Allocator* alloc, int bytes)
+{
+ Allocator_calloc(alloc, bytes, 1);
+ CanaryAllocator_check(alloc);
+}
+
+int main()
+{
+ struct Allocator* bAlloc;
+ BufferAllocator_STACK(bAlloc, 20000);
+
+ struct Allocator* alloc = CanaryAllocator_new(bAlloc, NULL);
+
+ tryCalloc(alloc, 0);
+ tryCalloc(alloc, 1);
+ tryCalloc(alloc, 2);
+ tryCalloc(alloc, 3);
+ tryCalloc(alloc, 4);
+ tryCalloc(alloc, 5);
+ tryCalloc(alloc, 6);
+ tryCalloc(alloc, 7);
+ tryCalloc(alloc, 8);
+ tryCalloc(alloc, 9);
+ tryCalloc(alloc, 10);
+ tryCalloc(alloc, 11);
+ tryCalloc(alloc, 12);
+
+ void* buff = Allocator_calloc(alloc, 1, 1);
+ CanaryAllocator_check(alloc);
+ Allocator_realloc(alloc, buff, 3);
+ CanaryAllocator_check(alloc);
+
+ tryOverflow(alloc, 0);
+ tryOverflow(alloc, 1);
+ tryOverflow(alloc, 2);
+ tryOverflow(alloc, 3);
+ tryOverflow(alloc, 4);
+ tryOverflow(alloc, 5);
+ tryOverflow(alloc, 6);
+ tryOverflow(alloc, 7);
+ tryOverflow(alloc, 8);
+ tryOverflow(alloc, 9);
+ tryOverflow(alloc, 10);
+ tryOverflow(alloc, 11);
+ tryOverflow(alloc, 12);
+}
Please sign in to comment.
Something went wrong with that request. Please try again.