diff --git a/src/liblzma/api/Makefile.am b/src/liblzma/api/Makefile.am index 86ce5bda..a36bf3ed 100644 --- a/src/liblzma/api/Makefile.am +++ b/src/liblzma/api/Makefile.am @@ -25,7 +25,6 @@ nobase_include_HEADERS = \ lzma/index_hash.h \ lzma/init.h \ lzma/lzma.h \ - lzma/memlimit.h \ lzma/simple.h \ lzma/stream_flags.h \ lzma/subblock.h \ diff --git a/src/liblzma/api/lzma.h b/src/liblzma/api/lzma.h index 0f109eb3..d954b8e1 100644 --- a/src/liblzma/api/lzma.h +++ b/src/liblzma/api/lzma.h @@ -208,7 +208,6 @@ extern "C" { #include "lzma/index.h" #include "lzma/index_hash.h" #include "lzma/stream_flags.h" -#include "lzma/memlimit.h" /* * All subheaders included. Undefine LZMA_H_INTERNAL to prevent applications diff --git a/src/liblzma/api/lzma/memlimit.h b/src/liblzma/api/lzma/memlimit.h deleted file mode 100644 index 836b0854..00000000 --- a/src/liblzma/api/lzma/memlimit.h +++ /dev/null @@ -1,207 +0,0 @@ -/** - * \file lzma/memlimit.h - * \brief Memory usage limiter - * - * \author Copyright (C) 1999-2006 Igor Pavlov - * \author Copyright (C) 2007 Lasse Collin - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - */ - -#ifndef LZMA_H_INTERNAL -# error Never include this file directly. Use instead. -#endif - - -/** - * \brief Opaque data type used with the memory usage limiting functions - */ -typedef struct lzma_memlimit_s lzma_memlimit; - - -/** - * \brief Allocates and initializes a new lzma_memlimit structure - * - * It is easy to make liblzma to use huge amounts of memory. This can - * be a problem especially with the decoder, since it a file requiring - * huge amounts of memory to uncompress could allow even a denial of - * service attack if the memory usage wasn't limited. - * - * liblzma provides a set of functions to control memory usage. Pointers - * to these functions can be used in lzma_allocator structure, which makes - * it easy to limit memory usage with liblzma. - * - * The memory limiter functions are not tied to limiting memory usage - * with liblzma itself. You can use them with anything you like. - * - * In multi-threaded applications, only one thread at once may use the same - * lzma_memlimit structure. If there is a need, this limitation may - * be removed in future versions without breaking the libary API/ABI. - * - * \param limit Initial memory usage limit in bytes - * - * \return Pointer to allocated and initialized lzma_memlimit - * structure. On error, NULL is returned. The reason behind - * an error is either that malloc() failed or that the given - * limit was so small that it didn't allow allocating even - * the lzma_memlimit structure itself. - * - * \note Excluding lzma_memlimit_usage(), the functions whose name begin - * lzma_memlimit_ can be used even if lzma_init() hasn't been - * called. - */ -extern lzma_memlimit *lzma_memlimit_create(size_t limit) - lzma_attr_warn_unused_result; - - -/** - * \brief Sets a new memory usage limit - * - * \param mem Pointer to a lzma_memlimit structure returned - * earlier by lzma_memry_limit_create(). - * \param limit New memory usage limit - * - * The new usage limit may be smaller than the amount of memory currently - * allocated via *mem: New allocations will fail until enough memory has - * been freed or a new limit is set, but the existing allocatations will - * stay untouched. - */ -extern void lzma_memlimit_set(lzma_memlimit *mem, size_t limit); - - -/** - * \brief Gets the current memory usage limit - */ -extern size_t lzma_memlimit_get(const lzma_memlimit *mem) - lzma_attr_pure; - - -/** - * \brief Gets the amount of currently allocated memory - * - * \note This value includes the sizes of some helper structures, - * thus it will always be larger than the total number of - * bytes allocated via lzma_memlimit_alloc(). - */ -extern size_t lzma_memlimit_used(const lzma_memlimit *mem) - lzma_attr_pure; - - -/** - * \brief Gets the maximum amount of memory required in total - * - * Returns how much memory was or would have been allocated at the same time. - * If lzma_memlimit_alloc() was requested so much memory that the limit - * would have been exceeded or malloc() simply ran out of memory, the - * requested amount is still included to the value returned by - * lzma_memlimit_max(). This may be used as a hint how much bigger memory - * limit would have been needed. - * - * If the clear flag is set, the internal variable holding the maximum - * value is set to the current memory usage (the same value as returned - * by lzma_memlimit_used()). - * - * \note Usually liblzma needs to allocate many chunks of memory, and - * displaying a message like "memory usage limit reached, at - * least 1024 bytes would have been needed" may be confusing, - * because the next allocation could have been e.g. 8 MiB. - * - * \todo The description of this function is unclear. - */ -extern size_t lzma_memlimit_max(lzma_memlimit *mem, lzma_bool clear); - - -/** - * \brief Checks if memory limit was reached at some point - * - * This function is useful to find out if the reason for LZMA_MEM_ERROR - * was running out of memory or hitting the memory usage limit imposed - * by lzma_memlimit_alloc(). If the clear argument is true, the internal - * flag, that indicates that limit was reached, is cleared. - */ -extern lzma_bool lzma_memlimit_reached(lzma_memlimit *mem, lzma_bool clear); - - -/** - * \brief Gets the number of allocations owned by the memory limiter - * - * The count does not include the helper structures; if no memory has - * been allocated with lzma_memlimit_alloc() or all memory allocated - * has been freed or detached, this will return zero. - */ -extern size_t lzma_memlimit_count(const lzma_memlimit *mem) - lzma_attr_pure; - - -/** - * \brief Allocates memory with malloc() if memory limit allows - * - * \param mem Pointer to a lzma_memlimit structure returned - * earlier by lzma_memry_limit_create(). - * \param nmemb Number of elements to allocate. While liblzma always - * sets this to one, this function still takes the - * value of nmemb into account to keep the function - * usable with zlib and libbzip2. - * \param size Size of an element. - * - * \return Pointer to memory allocated with malloc(nmemb * size), - * except if nmemb * size == 0 which returns malloc(1). - * On error, NULL is returned. - * - * \note This function assumes that nmemb * size is at maximum of - * SIZE_MAX. If it isn't, an overflow will occur resulting - * invalid amount of memory being allocated. - */ -extern void *lzma_memlimit_alloc( - lzma_memlimit *mem, size_t nmemb, size_t size) - lzma_attr_warn_unused_result; - - -/** - * \brief Removes the pointer from memory limiting list - * - * \param mem Pointer to a lzma_memlimit structure returned - * earlier by lzma_memry_limit_create(). - * \param ptr Pointer returned earlier by lzma_memlimit_alloc(). - * - * This function removes ptr from the internal list and decreases the - * counter of used memory accordingly. The ptr itself isn't freed. This is - * useful when Extra Records allocated by liblzma using lzma_memlimit - * are needed by the application and must not be freed when the - * lzma_memlimit structure is destroyed. - * - * It is OK to call this function with ptr that hasn't been allocated with - * lzma_memlimit_alloc(). In that case, this has no effect other than wasting - * a few CPU cycles. - */ -extern void lzma_memlimit_detach(lzma_memlimit *mem, void *ptr); - - -/** - * \brief Frees memory and updates the memory limit list - * - * This is like lzma_memlimit_detach() but also frees the given pointer. - */ -extern void lzma_memlimit_free(lzma_memlimit *mem, void *ptr); - - -/** - * \brief Frees the memory allocated for and by the memory usage limiter - * - * \param mem Pointer to memory limiter - * \param free_allocated If this is non-zero, all the memory allocated - * by lzma_memlimit_alloc() using *mem is also - * freed if it hasn't already been freed with - * lzma_memlimit_free(). Usually this should be - * set to true. - */ -extern void lzma_memlimit_end( - lzma_memlimit *mem, lzma_bool free_allocated); diff --git a/src/liblzma/common/Makefile.am b/src/liblzma/common/Makefile.am index 2f5532e4..e8794b5b 100644 --- a/src/liblzma/common/Makefile.am +++ b/src/liblzma/common/Makefile.am @@ -33,7 +33,6 @@ libcommon_la_SOURCES = \ index.c \ index.h \ init.c \ - memory_limiter.c \ stream_flags_common.c \ stream_flags_common.h \ vli_size.c diff --git a/src/liblzma/common/memory_limiter.c b/src/liblzma/common/memory_limiter.c deleted file mode 100644 index a2a0cbdc..00000000 --- a/src/liblzma/common/memory_limiter.c +++ /dev/null @@ -1,288 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -/// \file memory_limiter.c -/// \brief Limitting memory usage -// -// Copyright (C) 2007 Lasse Collin -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library 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 -// Lesser General Public License for more details. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "common.h" - - -/// Rounds an unsigned integer upwards to the next multiple. -#define my_ceil(num, multiple) \ - ((num) + (((multiple) - ((num) % (multiple))) % (multiple))) - - -/// Add approximated overhead of malloc() to size and round upwards to the -/// next multiple of 2 * sizeof(size_t). I suppose that most malloc() -/// implementations align small allocations this way, but the overhead -/// varies due to several reasons (free lists, mmap() usage etc.). -/// -/// This doesn't need to be exact at all. It's enough to take into account -/// that there is some overhead. That way our memory usage count won't be -/// horribly wrong if we are used to allocate lots of small memory chunks. -#define malloc_ceil(size) \ - my_ceil((size) + 2 * sizeof(void *), 2 * sizeof(size_t)) - - -typedef struct lzma_memlimit_list_s lzma_memlimit_list; -struct lzma_memlimit_list_s { - lzma_memlimit_list *next; - void *ptr; - size_t size; -}; - - -struct lzma_memlimit_s { - /// List of allocated memory chunks - lzma_memlimit_list *list; - - /// Number of bytes currently allocated; this includes the memory - /// needed for the helper structures. - size_t used; - - /// Memory usage limit - size_t limit; - - /// Maximum amount of memory that have been or would have been needed. - /// That is, this is updated also if memory allocation fails, letting - /// the application check how much memory was tried to be allocated - /// in total. - size_t max; - - /// True if lzma_memlimit_alloc() has returned NULL due to memory - /// usage limit. - bool limit_reached; -}; - - -extern LZMA_API lzma_memlimit * -lzma_memlimit_create(size_t limit) -{ - const size_t base_size = malloc_ceil(sizeof(lzma_memlimit)); - - if (limit < base_size) - return NULL; - - lzma_memlimit *mem = malloc(sizeof(lzma_memlimit)); - - if (mem != NULL) { - mem->list = NULL; - mem->used = base_size; - mem->limit = limit; - mem->max = base_size; - mem->limit_reached = false; - } - - return mem; -} - - -extern LZMA_API void -lzma_memlimit_set(lzma_memlimit *mem, size_t limit) -{ - mem->limit = limit; - return; -} - - -extern LZMA_API size_t -lzma_memlimit_get(const lzma_memlimit *mem) -{ - return mem->limit; -} - - -extern LZMA_API size_t -lzma_memlimit_used(const lzma_memlimit *mem) -{ - return mem->used; -} - - -extern LZMA_API size_t -lzma_memlimit_max(lzma_memlimit *mem, lzma_bool clear) -{ - const size_t ret = mem->max; - - if (clear) - mem->max = mem->used; - - return ret; -} - - -extern LZMA_API lzma_bool -lzma_memlimit_reached(lzma_memlimit *mem, lzma_bool clear) -{ - const bool ret = mem->limit_reached; - - if (clear) - mem->limit_reached = false; - - return ret; -} - - -extern LZMA_API size_t -lzma_memlimit_count(const lzma_memlimit *mem) -{ - // This is slow; we could have a counter in lzma_memlimit - // for fast version. I expect the primary use of this - // function to be limited to easy checking of memory leaks, - // in which this implementation is just fine. - size_t count = 0; - const lzma_memlimit_list *record = mem->list; - - while (record != NULL) { - ++count; - record = record->next; - } - - return count; -} - - -extern LZMA_API void -lzma_memlimit_end(lzma_memlimit *mem, lzma_bool free_allocated) -{ - if (mem == NULL) - return; - - lzma_memlimit_list *record = mem->list; - while (record != NULL) { - if (free_allocated) - free(record->ptr); - - lzma_memlimit_list *tmp = record; - record = record->next; - free(tmp); - } - - free(mem); - - return; -} - - -extern LZMA_API void * -lzma_memlimit_alloc(lzma_memlimit *mem, size_t nmemb, size_t size) -{ - // While liblzma always sets nmemb to one, do this multiplication - // to make these functions usable e.g. with zlib and libbzip2. - // Making sure that this doesn't overflow is up to the application. - size *= nmemb; - - // Some malloc() implementations return NULL on malloc(0). We like - // to get a non-NULL value. - if (size == 0) - size = 1; - - // Calculate how much memory we are going to allocate in reality. - const size_t total_size = malloc_ceil(size) - + malloc_ceil(sizeof(lzma_memlimit_list)); - - // Integer overflow protection for total_size and mem->used. - if (total_size <= size || SIZE_MAX - total_size < mem->used) { - mem->max = SIZE_MAX; - mem->limit_reached = true; - return NULL; - } - - // Update the maximum memory requirement counter if needed. This - // is updated even if memory allocation would fail or limit would - // be reached. - if (mem->used + total_size > mem->max) - mem->max = mem->used + total_size; - - // Check if we would stay in the memory usage limits. We need to - // check also that the current usage is in the limits, because - // the application could have decreased the limit between calls - // to this function. - if (mem->limit < mem->used || mem->limit - mem->used < total_size) { - mem->limit_reached = true; - return NULL; - } - - // Allocate separate memory chunks for lzma_memlimit_list and the - // actual requested memory. Optimizing this to use only one - // allocation is not a good idea, because applications may want to - // detach lzma_extra structures that have been allocated with - // lzma_memlimit_alloc(). - lzma_memlimit_list *record = malloc(sizeof(lzma_memlimit_list)); - void *ptr = malloc(size); - - if (record == NULL || ptr == NULL) { - free(record); - free(ptr); - return NULL; - } - - // Add the new entry to the beginning of the list. This should be - // more efficient when freeing memory, because usually it is - // "last allocated, first freed". - record->next = mem->list; - record->ptr = ptr; - record->size = total_size; - - mem->list = record; - mem->used += total_size; - - return ptr; -} - - -extern LZMA_API void -lzma_memlimit_detach(lzma_memlimit *mem, void *ptr) -{ - if (ptr == NULL || mem->list == NULL) - return; - - lzma_memlimit_list *record = mem->list; - lzma_memlimit_list *prev = NULL; - - while (record->ptr != ptr) { - prev = record; - record = record->next; - if (record == NULL) - return; - } - - if (prev != NULL) - prev->next = record->next; - else - mem->list = record->next; - - assert(mem->used >= record->size); - mem->used -= record->size; - - free(record); - - return; -} - - -extern LZMA_API void -lzma_memlimit_free(lzma_memlimit *mem, void *ptr) -{ - if (ptr == NULL) - return; - - lzma_memlimit_detach(mem, ptr); - - free(ptr); - - return; -} diff --git a/tests/Makefile.am b/tests/Makefile.am index 2d087e12..3e5c1be7 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -34,7 +34,6 @@ endif check_PROGRAMS = \ create_compress_files \ - test_memlimit \ test_check \ test_stream_flags \ test_filter_flags \ @@ -42,7 +41,6 @@ check_PROGRAMS = \ test_index TESTS = \ - test_memlimit \ test_check \ test_stream_flags \ test_filter_flags \ diff --git a/tests/test_memlimit.c b/tests/test_memlimit.c deleted file mode 100644 index 166e45b5..00000000 --- a/tests/test_memlimit.c +++ /dev/null @@ -1,114 +0,0 @@ -/////////////////////////////////////////////////////////////////////////////// -// -/// \file test_memlimit.c -/// \brief Tests the memory usage limiter -/// -/// \note These tests cannot be done at exact byte count accuracy, -/// because memory limiter takes into account the memory wasted -/// by bookkeeping structures and alignment (padding). -// -// Copyright (C) 2008 Lasse Collin -// -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. -// -// This library 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 -// Lesser General Public License for more details. -// -/////////////////////////////////////////////////////////////////////////////// - -#include "tests.h" - - -int -main(void) -{ - void *a; - void *b; - lzma_memlimit *mem; - - expect((mem = lzma_memlimit_create(1 << 16)) != NULL); - expect(lzma_memlimit_count(mem) == 0); - expect(lzma_memlimit_used(mem) > 0); - expect(lzma_memlimit_used(mem) < 4096); - expect(lzma_memlimit_used(mem) == lzma_memlimit_max(mem, false)); - expect(!lzma_memlimit_reached(mem, false)); - - expect((a = lzma_memlimit_alloc(mem, 1, 4096)) != NULL); - expect(lzma_memlimit_count(mem) == 1); - expect(lzma_memlimit_used(mem) > 4096); - expect(lzma_memlimit_used(mem) < 8192); - expect(lzma_memlimit_used(mem) == lzma_memlimit_max(mem, false)); - expect(!lzma_memlimit_reached(mem, false)); - - expect((b = lzma_memlimit_alloc(mem, 1, 4096)) != NULL); - expect(lzma_memlimit_count(mem) == 2); - expect(lzma_memlimit_used(mem) > 8192); - expect(lzma_memlimit_used(mem) < 12288); - expect(lzma_memlimit_used(mem) == lzma_memlimit_max(mem, false)); - expect(!lzma_memlimit_reached(mem, false)); - - expect((lzma_memlimit_alloc(mem, 1, 1 << 17)) == NULL); - expect(lzma_memlimit_count(mem) == 2); - expect(lzma_memlimit_used(mem) > 8192); - expect(lzma_memlimit_used(mem) < 12288); - expect(lzma_memlimit_used(mem) < lzma_memlimit_max(mem, false)); - expect(lzma_memlimit_max(mem, false) > (1 << 17)); - expect(lzma_memlimit_reached(mem, false)); - - lzma_memlimit_free(mem, a); - expect(lzma_memlimit_count(mem) == 1); - expect(lzma_memlimit_used(mem) > 4096); - expect(lzma_memlimit_used(mem) < 8192); - expect(lzma_memlimit_max(mem, true) > (1 << 17)); - expect(lzma_memlimit_reached(mem, true)); - expect(lzma_memlimit_used(mem) == lzma_memlimit_max(mem, false)); - expect(!lzma_memlimit_reached(mem, false)); - - expect(lzma_memlimit_get(mem) == 1 << 16); - lzma_memlimit_set(mem, 6144); - expect(lzma_memlimit_get(mem) == 6144); - expect(lzma_memlimit_alloc(mem, 1, 4096) == NULL); - expect(lzma_memlimit_max(mem, false) > 8192); - expect(lzma_memlimit_reached(mem, false)); - - lzma_memlimit_free(mem, b); - expect(lzma_memlimit_count(mem) == 0); - expect(lzma_memlimit_used(mem) > 0); - expect(lzma_memlimit_used(mem) < 4096); - - expect((a = lzma_memlimit_alloc(mem, 1, 4096)) != NULL); - expect(lzma_memlimit_count(mem) == 1); - expect(lzma_memlimit_used(mem) > 4096); - expect(lzma_memlimit_used(mem) < 8192); - - expect(lzma_memlimit_max(mem, false) > 8192); - expect(lzma_memlimit_reached(mem, false)); - expect(lzma_memlimit_max(mem, true) > 8192); - expect(lzma_memlimit_reached(mem, true)); - expect(lzma_memlimit_max(mem, true) < 8192); - expect(!lzma_memlimit_reached(mem, true)); - - lzma_memlimit_detach(mem, a); - free(a); - expect(lzma_memlimit_count(mem) == 0); - - lzma_memlimit_set(mem, SIZE_MAX); - expect(lzma_memlimit_alloc(mem, 1, SIZE_MAX - 33) == NULL); - expect(lzma_memlimit_count(mem) == 0); - expect(lzma_memlimit_max(mem, true) == SIZE_MAX); - expect(lzma_memlimit_reached(mem, true)); - - expect(lzma_memlimit_alloc(mem, 1, SIZE_MAX) == NULL); - expect(lzma_memlimit_count(mem) == 0); - expect(lzma_memlimit_max(mem, false) == SIZE_MAX); - expect(lzma_memlimit_reached(mem, false)); - - lzma_memlimit_end(mem, true); - - return 0; -}