There is no functional change on other OSes.
See the comment in my_allocator.h.
Reported-by: Harry Sintonen <sintonen@iki.fi>
Fixes: https://github.com/tukaani-project/xz/pull/181
--- /dev/null
+// SPDX-License-Identifier: 0BSD
+
+///////////////////////////////////////////////////////////////////////////////
+//
+/// \file my_allocator.h
+/// \brief lzma_allocator to use malloc() and free() from app's libc
+///
+/// In 1980s and early 1990s, some operating systems implemented shared
+/// libraries so that not only the code but also the data is shared
+/// between processes. If an application allocates memory by calling into
+/// a shared library, it also needs to call into the library to free the
+/// memory before the application terminates. Otherwise memory is leaked
+/// in the shared library.
+///
+/// The memory allocated by the application itself *is* freed on those
+/// OSes when the application terminates. It's only the memory (and other
+/// resources) allocated in shared libraries that can be a problem. If
+/// a shared library is made to use application's malloc() and free(),
+/// the problem is solved as long as the shared library doesn't also
+/// allocate some other types of resources. Thus, this kind of leak
+/// prevention is incompatible with threads.
+///
+/// A related issue is that if the shared library allocates memory with
+/// malloc() and the resulting pointer is passed to the application, the
+/// application cannot free the memory using free(). This is because the
+/// shared library and application have their own heaps. This too is
+/// solved if the shared library is made to use application's malloc()
+/// and free(). (This issue is possible on Windows even in modern times
+/// if a DLL uses a different CRT than the application. However, it is
+/// reasonable to assume that XZ Utils components all use the same CRT.)
+///
+/// The allocator in this header is a hack that should only be enabled
+/// on those OSes that really need it. liblzma uses not only malloc() and
+/// free() but also calloc(). When a custom allocator is used, calloc()
+/// is replaced with allocator->alloc() + memset() which is significantly
+/// slower in certain situations where most of the allocated memory isn't
+/// actually needed (compressing a tiny file with the LZMA2 dictionary
+/// size set to a large value).
+//
+// Author: Lasse Collin
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifndef MY_ALLOCATOR_H
+#define MY_ALLOCATOR_H
+
+#include "sysdefs.h"
+#include "lzma.h"
+
+
+#ifdef __MORPHOS__
+
+static void * LZMA_API_CALL
+my_alloc(void *opaque lzma_attribute((__unused__)), size_t nmemb, size_t size)
+{
+ // liblzma guarantees that this won't overflow.
+ return malloc(nmemb * size);
+}
+
+static void LZMA_API_CALL
+my_free(void *opaque lzma_attribute((__unused__)), void *ptr)
+{
+ free(ptr);
+}
+
+static const lzma_allocator my_allocator = { &my_alloc, &my_free, NULL };
+
+#define MY_ALLOCATOR (&my_allocator)
+#define MY_ALLOCATOR_SET(strm) ((strm).allocator = &my_allocator)
+
+#else
+
+// OSes with modern shared library mechanism don't need the allocator hack.
+// For example, this isn't needed on Windows 95.
+#define MY_ALLOCATOR NULL
+#define MY_ALLOCATOR_SET(strm) ((void)0)
+
+#endif
+
+#endif
#include "tuklib_mbstr_nonprint.h"
#include "tuklib_mbstr_wrap.h"
#include "tuklib_exit.h"
+#include "my_allocator.h"
#ifdef TUKLIB_DOSLIKE
# include <fcntl.h>
lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
// Parse the first five bytes.
- switch (lzma_properties_decode(&filter, NULL, buf, 5)) {
+ switch (lzma_properties_decode(&filter, MY_ALLOCATOR, buf, 5)) {
case LZMA_OK:
break;
#include "private.h"
#include "tuklib_integer.h"
+#include "my_allocator.h"
/// Return value type for coder_init().
// Setting a preset or using --filters makes us forget
// the earlier custom filter chain (if any).
if (filters_count > 0) {
- lzma_filters_free(chains[0], NULL);
+ lzma_filters_free(chains[0], MY_ALLOCATOR);
filters_count = 0;
}
{
int error_pos;
const char *err = lzma_str_to_filters(str, &error_pos,
- chains[index], flags, NULL);
+ chains[index], flags, MY_ALLOCATOR);
if (err != NULL) {
char filter_num[2] = "";
{
// Free old filters first, if they were previously allocated.
if (chains_used_mask & (1U << slot))
- lzma_filters_free(chains[slot], NULL);
+ lzma_filters_free(chains[slot], MY_ALLOCATOR);
str_to_filters(str, slot, 0);
// Decode the LZMA1 properties.
lzma_filter filter = { .id = LZMA_FILTER_LZMA1 };
- if (lzma_properties_decode(&filter, NULL, in_buf.u8, 5) != LZMA_OK)
+ if (lzma_properties_decode(&filter, MY_ALLOCATOR, in_buf.u8, 5)
+ != LZMA_OK)
return false;
// A hack to ditch tons of false positives: We allow only dictionary
? chains[0]
: chains[opt_block_list[0].chain_num];
+ // See src/common/my_allocator.h.
+ MY_ALLOCATOR_SET(strm);
+
if (opt_mode == MODE_COMPRESS) {
#ifdef HAVE_ENCODERS
switch (opt_format) {
// don't worry about freeing it.
for (uint32_t i = 1; i < ARRAY_SIZE(chains); i++) {
if (chains_used_mask & (1U << i))
- lzma_filters_free(chains[i], NULL);
+ lzma_filters_free(chains[i], MY_ALLOCATOR);
}
lzma_end(&strm);
#include "private.h"
#include "tuklib_integer.h"
+#include "my_allocator.h"
/// Information about a .xz file
io_buf buf;
lzma_stream strm = LZMA_STREAM_INIT;
+ MY_ALLOCATOR_SET(strm);
lzma_index *idx = NULL;
lzma_ret ret = lzma_file_info_decoder(&strm, &idx,
goto data_error;
// Decode the Block Header.
- switch (lzma_block_header_decode(&block, NULL, buf.u8)) {
+ switch (lzma_block_header_decode(&block, MY_ALLOCATOR, buf.u8)) {
case LZMA_OK:
break;
case LZMA_DATA_ERROR:
// Free the memory allocated by lzma_block_header_decode().
- lzma_filters_free(filters, NULL);
+ lzma_filters_free(filters, MY_ALLOCATOR);
goto data_error;
default:
// Convert the filter chain to human readable form.
const lzma_ret str_ret = lzma_str_from_filters(
&bhi->filter_chain, filters,
- LZMA_STR_DECODER | LZMA_STR_GETOPT_LONG, NULL);
+ LZMA_STR_DECODER | LZMA_STR_GETOPT_LONG, MY_ALLOCATOR);
// Free the memory allocated by lzma_block_header_decode().
- lzma_filters_free(filters, NULL);
+ lzma_filters_free(filters, MY_ALLOCATOR);
// Check if the stringification succeeded.
if (str_ret != LZMA_OK) {
if (!fail)
update_totals(&xfi);
- lzma_index_end(xfi.idx, NULL);
+ lzma_index_end(xfi.idx, MY_ALLOCATOR);
}
io_close(pair, false);
#include "private.h"
#include "tuklib_mbstr_wrap.h"
+#include "my_allocator.h"
#include <stdarg.h>
char *buf;
const lzma_ret ret = lzma_str_from_filters(&buf, filters,
- LZMA_STR_ENCODER | LZMA_STR_GETOPT_LONG, NULL);
+ LZMA_STR_ENCODER | LZMA_STR_GETOPT_LONG, MY_ALLOCATOR);
if (ret != LZMA_OK)
message_fatal("%s", message_strm(ret));
char *encoder_options;
if (lzma_str_list_filters(&encoder_options, LZMA_VLI_UNKNOWN,
- LZMA_STR_ENCODER, NULL) != LZMA_OK)
+ LZMA_STR_ENCODER, MY_ALLOCATOR) != LZMA_OK)
message_bug();
if (!opt_robot) {
#include "tuklib_progname.h"
#include "tuklib_mbstr_nonprint.h"
#include "tuklib_exit.h"
+#include "my_allocator.h"
#ifdef TUKLIB_DOSLIKE
# include <fcntl.h>
// we don't need to reallocate memory for every file if they use same
// compression settings.
lzma_stream strm = LZMA_STREAM_INIT;
+ MY_ALLOCATOR_SET(strm);
// Some systems require setting stdin and stdout to binary mode.
#ifdef TUKLIB_DOSLIKE