Explorar el Código

Fixed segmentation fault in `DynamicJsonBuffer` when memory allocation fails (issue #92)

Benoit Blanchon hace 10 años
padre
commit
2524a00a96

+ 3 - 0
.clang-format

@@ -0,0 +1,3 @@
+# http://clang.llvm.org/docs/ClangFormatStyleOptions.html
+
+BasedOnStyle: Google

+ 1 - 0
.gitattributes

@@ -0,0 +1 @@
+*.sh text eol=lf

+ 5 - 0
CHANGELOG.md

@@ -1,6 +1,11 @@
 Arduino JSON: change log
 Arduino JSON: change log
 ========================
 ========================
 
 
+v4.6
+----
+
+* Fixed segmentation fault in `DynamicJsonBuffer` when memory allocation fails (issue #92)
+
 v4.5
 v4.5
 ----
 ----
 
 

+ 3 - 32
include/ArduinoJson/DynamicJsonBuffer.hpp

@@ -6,41 +6,12 @@
 
 
 #pragma once
 #pragma once
 
 
-#include "JsonBuffer.hpp"
-
-#include <stdlib.h>
+#include "Internals/BlockJsonBuffer.hpp"
 
 
 namespace ArduinoJson {
 namespace ArduinoJson {
-
-// Forward declaration
-namespace Internals {
-struct DynamicJsonBufferBlock;
-}
-
 // Implements a JsonBuffer with dynamic memory allocation.
 // Implements a JsonBuffer with dynamic memory allocation.
 // You are strongly encouraged to consider using StaticJsonBuffer which is much
 // You are strongly encouraged to consider using StaticJsonBuffer which is much
 // more suitable for embedded systems.
 // more suitable for embedded systems.
-class DynamicJsonBuffer : public JsonBuffer {
- public:
-  DynamicJsonBuffer();
-  ~DynamicJsonBuffer();
-
-  size_t size() const;
-
- protected:
-  virtual void* alloc(size_t bytes);
-
- private:
-  typedef Internals::DynamicJsonBufferBlock Block;
-
-  static const size_t FIRST_BLOCK_CAPACITY = 32;
-
-  static Block* createBlock(size_t capacity);
-
-  inline bool canAllocInHead(size_t bytes) const;
-  inline void* allocInHead(size_t bytes);
-  inline void addNewBlock();
-
-  Block* _head;
-};
+typedef Internals::BlockJsonBuffer<Internals::DefaultAllocator>
+    DynamicJsonBuffer;
 }
 }

+ 93 - 0
include/ArduinoJson/Internals/BlockJsonBuffer.hpp

@@ -0,0 +1,93 @@
+// Copyright Benoit Blanchon 2014-2015
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+
+#pragma once
+
+#include "../JsonBuffer.hpp"
+
+#include <stdlib.h>
+
+namespace ArduinoJson {
+namespace Internals {
+class DefaultAllocator {
+ public:
+  void* allocate(size_t size) { return malloc(size); }
+  void deallocate(void* pointer) { free(pointer); }
+};
+
+template <typename TAllocator>
+class BlockJsonBuffer : public JsonBuffer {
+  struct Block;
+  struct EmptyBlock {
+    Block* next;
+    size_t capacity;
+    size_t size;
+  };
+  struct Block : EmptyBlock {
+    uint8_t data[1];
+  };
+
+ public:
+  BlockJsonBuffer() : _head(NULL) {}
+
+  ~BlockJsonBuffer() {
+    Block* currentBlock = _head;
+
+    while (currentBlock != NULL) {
+      Block* nextBlock = currentBlock->next;
+      _allocator.deallocate(currentBlock);
+      currentBlock = nextBlock;
+    }
+  }
+
+  size_t size() const {
+    size_t total = 0;
+    for (const Block* b = _head; b; b = b->next) total += b->size;
+    return total;
+  }
+
+ protected:
+  virtual void* alloc(size_t bytes) {
+    return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes);
+  }
+
+ private:
+  static const size_t FIRST_BLOCK_CAPACITY = 32;
+
+  bool canAllocInHead(size_t bytes) const {
+    return _head != NULL && _head->size + bytes <= _head->capacity;
+  }
+
+  void* allocInHead(size_t bytes) {
+    void* p = _head->data + _head->size;
+    _head->size += bytes;
+    return p;
+  }
+
+  void* allocInNewBlock(size_t bytes) {
+    size_t capacity = FIRST_BLOCK_CAPACITY;
+    if (_head != NULL) capacity = _head->capacity * 2;
+    if (bytes > capacity) capacity = bytes;
+    if (!addNewBlock(capacity)) return NULL;
+    return allocInHead(bytes);
+  }
+
+  bool addNewBlock(size_t capacity) {
+    size_t size = sizeof(EmptyBlock) + capacity;
+    Block* block = static_cast<Block*>(_allocator.allocate(size));
+    if (block == NULL) return false;
+    block->capacity = capacity;
+    block->size = 0;
+    block->next = _head;
+    _head = block;
+    return true;
+  }
+
+  Block* _head;
+  TAllocator _allocator;
+};
+}
+}

+ 21 - 22
scripts/build-arduino-package.sh

@@ -1,23 +1,22 @@
-#!/bin/bash
-
-ZIP="C:\Program Files\7-Zip\7z.exe"
-TAG=$(git describe)
-OUTPUT="ArduinoJson-$TAG.zip"
-
-cd ../..
-
-# remove existing file
-rm -f $OUTPUT
-
-# create zip
-"$ZIP" a $OUTPUT \
-	ArduinoJson/CHANGELOG.md \
-	ArduinoJson/examples \
-	ArduinoJson/include \
-	ArduinoJson/keywords.txt \
-	ArduinoJson/LICENSE.md \
-	ArduinoJson/README.md \
-	ArduinoJson/src	\
-	ArduinoJson/ArduinoJson.h \
-	ArduinoJson/ArduinoJson.cpp	\
+#!/bin/bash
+
+TAG=$(git describe)
+OUTPUT="ArduinoJson-$TAG.zip"
+
+cd ../..
+
+# remove existing file
+rm -f $OUTPUT
+
+# create zip
+7z a $OUTPUT \
+	ArduinoJson/CHANGELOG.md \
+	ArduinoJson/examples \
+	ArduinoJson/include \
+	ArduinoJson/keywords.txt \
+	ArduinoJson/LICENSE.md \
+	ArduinoJson/README.md \
+	ArduinoJson/src	\
+	ArduinoJson/ArduinoJson.h \
+	ArduinoJson/ArduinoJson.cpp	\
     -x!ArduinoJson/src/CMakeLists.txt
     -x!ArduinoJson/src/CMakeLists.txt

+ 0 - 79
src/DynamicJsonBuffer.cpp

@@ -1,79 +0,0 @@
-// Copyright Benoit Blanchon 2014-2015
-// MIT License
-//
-// Arduino JSON library
-// https://github.com/bblanchon/ArduinoJson
-
-#include "../include/ArduinoJson/DynamicJsonBuffer.hpp"
-
-namespace ArduinoJson {
-namespace Internals {
-struct DynamicJsonBufferBlockWithoutData {
-  DynamicJsonBufferBlock* next;
-  size_t capacity;
-  size_t size;
-};
-
-
-struct DynamicJsonBufferBlock : DynamicJsonBufferBlockWithoutData {
-  uint8_t data[1];
-};
-}
-}
-
-using namespace ArduinoJson;
-using namespace ArduinoJson::Internals;
-
-DynamicJsonBuffer::DynamicJsonBuffer() {
-  _head = createBlock(FIRST_BLOCK_CAPACITY);
-}
-
-DynamicJsonBuffer::~DynamicJsonBuffer() {
-  Block* currentBlock = _head;
-
-  while (currentBlock != NULL) {
-    Block* nextBlock = currentBlock->next;
-    free(currentBlock);
-    currentBlock = nextBlock;
-  }
-}
-
-size_t DynamicJsonBuffer::size() const {
-  size_t total = 0;
-
-  for (const Block* b = _head; b != NULL; b = b->next) {
-    total += b->size;
-  }
-
-  return total;
-}
-
-void* DynamicJsonBuffer::alloc(size_t bytes) {
-  if (!canAllocInHead(bytes)) addNewBlock();
-  return allocInHead(bytes);
-}
-
-bool DynamicJsonBuffer::canAllocInHead(size_t bytes) const {
-  return _head->size + bytes <= _head->capacity;
-}
-
-void* DynamicJsonBuffer::allocInHead(size_t bytes) {
-  void* p = _head->data + _head->size;
-  _head->size += bytes;
-  return p;
-}
-
-void DynamicJsonBuffer::addNewBlock() {
-  Block* block = createBlock(_head->capacity * 2);
-  block->next = _head;
-  _head = block;
-}
-
-DynamicJsonBuffer::Block* DynamicJsonBuffer::createBlock(size_t capacity) {
-  size_t blkSize = sizeof(DynamicJsonBufferBlockWithoutData) + capacity;
-  Block* block = static_cast<Block*>(malloc(blkSize));
-  block->capacity = capacity;
-  block->size = 0;
-  block->next = NULL;
-  return block;
-}

+ 37 - 0
test/DynamicJsonBuffer_NoMemory_Tests.cpp

@@ -0,0 +1,37 @@
+// Copyright Benoit Blanchon 2014-2015
+// MIT License
+//
+// Arduino JSON library
+// https://github.com/bblanchon/ArduinoJson
+
+#include <gtest/gtest.h>
+#include <ArduinoJson.h>
+
+class DynamicJsonBuffer_NoMemory_Tests : public ::testing::Test {
+  class NoMemoryAllocator {
+   public:
+    void* allocate(size_t) { return NULL; }
+    void deallocate(void*) {}
+  };
+
+ protected:
+  Internals::BlockJsonBuffer<NoMemoryAllocator> _jsonBuffer;
+};
+
+TEST_F(DynamicJsonBuffer_NoMemory_Tests, CreateArray) {
+  ASSERT_FALSE(_jsonBuffer.createArray().success());
+}
+
+TEST_F(DynamicJsonBuffer_NoMemory_Tests, CreateObject) {
+  ASSERT_FALSE(_jsonBuffer.createObject().success());
+}
+
+TEST_F(DynamicJsonBuffer_NoMemory_Tests, ParseArray) {
+  char json[] = "[]";
+  ASSERT_FALSE(_jsonBuffer.parseArray(json).success());
+}
+
+TEST_F(DynamicJsonBuffer_NoMemory_Tests, ParseObject) {
+  char json[] = "{}";
+  ASSERT_FALSE(_jsonBuffer.parseObject(json).success());
+}