From 7a2e1a0a7a575c64eaf050c8ec08e5b36e4acfad Mon Sep 17 00:00:00 2001
From: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
Date: Thu, 8 Apr 2021 12:04:33 +0200
Subject: [PATCH 4/5] Fix: libsquashfs: add sqfs_free() function

On systems like Windows, the dynamic library and applications can
easily end up being linked against different runtime libraries, so
applications cannot be expected to be able to free() any malloc'd
pointer that the library returns.

This commit adds an sqfs_free function so the application can pass
pointers back to the library to call the correct free() implementation.

Signed-off-by: David Oberhollenzer <david.oberhollenzer@sigma-star.at>
---
 bin/rdsquashfs/dump_xattrs.c    |  6 ++---
 bin/rdsquashfs/restore_fstree.c |  6 ++---
 bin/rdsquashfs/stat.c           |  2 +-
 bin/sqfs2tar/xattr.c            |  6 ++---
 extras/browse.c                 | 20 +++++++--------
 extras/mk42sqfs.c               |  2 +-
 extras/mknastyfs.c              |  2 +-
 include/sqfs/data_reader.h      |  4 +--
 include/sqfs/dir_reader.h       |  8 +++---
 include/sqfs/inode.h            |  3 ++-
 include/sqfs/meta_reader.h      |  4 +--
 include/sqfs/predef.h           | 43 +++++++++++++++++++++++++++++++++
 include/sqfs/xattr_reader.h     |  4 +--
 lib/sqfs/Makemodule.am          |  1 +
 lib/sqfs/misc.c                 | 17 +++++++++++++
 15 files changed, 95 insertions(+), 33 deletions(-)
 create mode 100644 lib/sqfs/misc.c

diff --git a/bin/rdsquashfs/dump_xattrs.c b/bin/rdsquashfs/dump_xattrs.c
index 93b0b01..9dbe437 100644
--- a/bin/rdsquashfs/dump_xattrs.c
+++ b/bin/rdsquashfs/dump_xattrs.c
@@ -95,7 +95,7 @@ int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode)
 
 		if (sqfs_xattr_reader_read_value(xattr, key, &value)) {
 			fputs("Error reading xattr value\n", stderr);
-			free(key);
+			sqfs_free(key);
 			return -1;
 		}
 
@@ -112,8 +112,8 @@ int dump_xattrs(sqfs_xattr_reader_t *xattr, const sqfs_inode_generic_t *inode)
 			printf("\n");
 		}
 
-		free(key);
-		free(value);
+		sqfs_free(key);
+		sqfs_free(value);
 	}
 
 	return 0;
diff --git a/bin/rdsquashfs/restore_fstree.c b/bin/rdsquashfs/restore_fstree.c
index 3d536b6..57582c2 100644
--- a/bin/rdsquashfs/restore_fstree.c
+++ b/bin/rdsquashfs/restore_fstree.c
@@ -188,7 +188,7 @@ static int set_xattr(const char *path, sqfs_xattr_reader_t *xattr,
 
 		if (sqfs_xattr_reader_read_value(xattr, key, &value)) {
 			fputs("Error reading xattr value\n", stderr);
-			free(key);
+			sqfs_free(key);
 			return -1;
 		}
 
@@ -199,8 +199,8 @@ static int set_xattr(const char *path, sqfs_xattr_reader_t *xattr,
 				key->key, path, strerror(errno));
 		}
 
-		free(key);
-		free(value);
+		sqfs_free(key);
+		sqfs_free(value);
 		if (ret)
 			return -1;
 	}
diff --git a/bin/rdsquashfs/stat.c b/bin/rdsquashfs/stat.c
index 049baae..a5aab60 100644
--- a/bin/rdsquashfs/stat.c
+++ b/bin/rdsquashfs/stat.c
@@ -172,7 +172,7 @@ int stat_file(const sqfs_tree_node_t *node)
 			       idx->size + 1, idx->name,
 			       idx->start_block, idx->index);
 
-			free(idx);
+			sqfs_free(idx);
 		}
 		break;
 	}
diff --git a/bin/sqfs2tar/xattr.c b/bin/sqfs2tar/xattr.c
index 9a9ec82..abec4fb 100644
--- a/bin/sqfs2tar/xattr.c
+++ b/bin/sqfs2tar/xattr.c
@@ -68,13 +68,13 @@ int get_xattrs(const char *name, const sqfs_inode_generic_t *inode,
 		ret = sqfs_xattr_reader_read_value(xr, key, &value);
 		if (ret) {
 			sqfs_perror(name, "reading xattr value", ret);
-			free(key);
+			sqfs_free(key);
 			goto fail;
 		}
 
 		ent = mkxattr(key, value);
-		free(key);
-		free(value);
+		sqfs_free(key);
+		sqfs_free(value);
 
 		if (ent == NULL)
 			goto fail;
diff --git a/extras/browse.c b/extras/browse.c
index 86ce788..8b96c15 100644
--- a/extras/browse.c
+++ b/extras/browse.c
@@ -70,12 +70,12 @@ static void list_directory(const char *dirname)
 		sqfs_dir_reader_get_root_inode(dr, &root);
 
 		ret = sqfs_dir_reader_find_by_path(dr, root, dirname, &inode);
-		free(root);
+		sqfs_free(root);
 		if (ret)
 			goto fail_resolve;
 
 		ret = sqfs_dir_reader_open_dir(dr, inode, 0);
-		free(inode);
+		sqfs_free(inode);
 		if (ret)
 			goto fail_open;
 	} else {
@@ -85,7 +85,7 @@ static void list_directory(const char *dirname)
 			goto fail_resolve;
 
 		ret = sqfs_dir_reader_open_dir(dr, inode, 0);
-		free(inode);
+		sqfs_free(inode);
 		if (ret)
 			goto fail_open;
 	}
@@ -102,7 +102,7 @@ static void list_directory(const char *dirname)
 		}
 
 		len = ent->size + 1;
-		free(ent);
+		sqfs_free(ent);
 	}
 
 	col_count = 79 / (max_len + 1);
@@ -148,7 +148,7 @@ static void list_directory(const char *dirname)
 
 		printf("%.*s", ent->size + 1, ent->name);
 		fputs("\033[0m", stdout);
-		free(ent);
+		sqfs_free(ent);
 
 		++i;
 		if (i == col_count) {
@@ -231,7 +231,7 @@ static void stat_cmd(const char *filename)
 	if (*filename == '/') {
 		sqfs_dir_reader_get_root_inode(dr, &root);
 		ret = sqfs_dir_reader_find_by_path(dr, root, filename, &inode);
-		free(root);
+		sqfs_free(root);
 		if (ret)
 			goto fail_resolve;
 	} else {
@@ -398,12 +398,12 @@ static void stat_cmd(const char *filename)
 			printf("\tSize: %u\n", idx->size + 1);
 			printf("\tEntry: %.*s\n\n", idx->size + 1, idx->name);
 
-			free(idx);
+			sqfs_free(idx);
 		}
 		break;
 	}
 
-	free(inode);
+	sqfs_free(inode);
 	return;
 fail_resolve:
 	printf("Error resolving '%s', error code %d\n", filename, ret);
@@ -429,7 +429,7 @@ static void cat_cmd(const char *filename)
 	if (*filename == '/') {
 		sqfs_dir_reader_get_root_inode(dr, &root);
 		ret = sqfs_dir_reader_find_by_path(dr, root, filename, &inode);
-		free(root);
+		sqfs_free(root);
 	} else {
 		ret = sqfs_dir_reader_find_by_path(dr, working_dir,
 						   filename, &inode);
@@ -459,7 +459,7 @@ static void cat_cmd(const char *filename)
 		offset += diff;
 	}
 
-	free(inode);
+	sqfs_free(inode);
 }
 
 /*****************************************************************************/
diff --git a/extras/mk42sqfs.c b/extras/mk42sqfs.c
index 92323e0..08dffe6 100644
--- a/extras/mk42sqfs.c
+++ b/extras/mk42sqfs.c
@@ -167,7 +167,7 @@ int main(void)
 	sqfs_meta_writer_get_position(inode_m, &block_start, &offset);
 	super.root_inode_ref = (block_start << 16) | offset;
 	sqfs_meta_writer_write_inode(inode_m, inode);
-	free(inode);
+	sqfs_free(inode);
 
 	/* flush the meta data to the file */
 	sqfs_meta_writer_flush(inode_m);
diff --git a/extras/mknastyfs.c b/extras/mknastyfs.c
index daadf37..20074d3 100644
--- a/extras/mknastyfs.c
+++ b/extras/mknastyfs.c
@@ -146,7 +146,7 @@ int main(void)
 	sqfs_meta_writer_get_position(inode_m, &block_start, &offset);
 	super.root_inode_ref = (block_start << 16) | offset;
 	sqfs_meta_writer_write_inode(inode_m, inode);
-	free(inode);
+	sqfs_free(inode);
 
 	/* flush the meta data to the file */
 	sqfs_meta_writer_flush(inode_m);
diff --git a/include/sqfs/data_reader.h b/include/sqfs/data_reader.h
index c967a70..29077b7 100644
--- a/include/sqfs/data_reader.h
+++ b/include/sqfs/data_reader.h
@@ -94,7 +94,7 @@ SQFS_API int sqfs_data_reader_load_fragment_table(sqfs_data_reader_t *data,
  * @param inode A pointer to the inode describing the file.
  * @param size Returns the size of the data read.
  * @param out Returns a pointer to the raw data that must be
- *            released using free.
+ *            released using @ref sqfs_free.
  *
  * @return Zero on succcess, an @ref SQFS_ERROR value on failure.
  */
@@ -112,7 +112,7 @@ SQFS_API int sqfs_data_reader_get_fragment(sqfs_data_reader_t *data,
  * @param index The block index in the inodes block list.
  * @param size Returns the size of the data read.
  * @param out Returns a pointer to the raw data that must be
- *            released using free.
+ *            released using @ref sqfs_free.
  *
  * @return Zero on succcess, an @ref SQFS_ERROR value on failure.
  */
diff --git a/include/sqfs/dir_reader.h b/include/sqfs/dir_reader.h
index cf9f2a1..3bef7e3 100644
--- a/include/sqfs/dir_reader.h
+++ b/include/sqfs/dir_reader.h
@@ -231,7 +231,7 @@ SQFS_API int sqfs_dir_reader_find(sqfs_dir_reader_t *rd, const char *name);
  *
  * @param rd A pointer to a directory reader.
  * @param out Returns a pointer to a directory entry on success that can be
- *            freed with a single free call.
+ *            freed with a single @ref sqfs_free call.
  *
  * @return Zero on success, an @ref SQFS_ERROR value on failure, a positive
  *         number if the end of the current directory listing has been reached.
@@ -246,7 +246,7 @@ SQFS_API int sqfs_dir_reader_read(sqfs_dir_reader_t *rd,
  *
  * @param rd A pointer to a directory reader.
  * @param out Returns a pointer to a generic inode that can be freed with a
- *            single free call.
+ *            single @ref sqfs_free call.
  *
  * @return Zero on success, an @ref SQFS_ERROR value on failure.
  */
@@ -260,7 +260,7 @@ SQFS_API int sqfs_dir_reader_get_inode(sqfs_dir_reader_t *rd,
  *
  * @param rd A pointer to a directory reader.
  * @param out Returns a pointer to a generic inode that can be freed with a
- *            single free call.
+ *            single @ref sqfs_free call.
  *
  * @return Zero on success, an @ref SQFS_ERROR value on failure.
  */
@@ -280,7 +280,7 @@ SQFS_API int sqfs_dir_reader_get_root_inode(sqfs_dir_reader_t *rd,
  *             be used to separate path components. Resolving '.' or '..' is
  *             not supported.
  * @param out Returns a pointer to a generic inode that can be freed with a
- *            single free call.
+ *            single @ref sqfs_free call.
  *
  * @return Zero on success, an @ref SQFS_ERROR value on failure.
  */
diff --git a/include/sqfs/inode.h b/include/sqfs/inode.h
index 5359b54..0bc33db 100644
--- a/include/sqfs/inode.h
+++ b/include/sqfs/inode.h
@@ -718,7 +718,8 @@ SQFS_API int sqfs_inode_get_file_block_start(const sqfs_inode_generic_t *inode,
  * fiddles the raw data out into a propperly aligned, external structure.
  *
  * @param inode A pointer to an inode.
- * @param out Returns the index entry. Can be freed with a single free call.
+ * @param out Returns the index entry. Can be freed with a single
+ *            @ref sqfs_free call.
  * @param index An index value between 0 and inodex_count.
  *
  * @return Zero on success, @ref SQFS_ERROR_OUT_OF_BOUNDS if the given index
diff --git a/include/sqfs/meta_reader.h b/include/sqfs/meta_reader.h
index bac1808..e0dc186 100644
--- a/include/sqfs/meta_reader.h
+++ b/include/sqfs/meta_reader.h
@@ -145,7 +145,7 @@ SQFS_API int sqfs_meta_reader_read_dir_header(sqfs_meta_reader_t *m,
  *
  * @param m A pointer to a meta data reader.
  * @param ent Returns a pointer to a directory entry. Can be released with a
- *            single free() call.
+ *            single @ref sqfs_free call.
  *
  * @return Zero on success, an @ref SQFS_ERROR value on failure.
  */
@@ -167,7 +167,7 @@ SQFS_API int sqfs_meta_reader_read_dir_ent(sqfs_meta_reader_t *m,
  * @param offset A byte offset within the uncompressed block where the
  *               inode is.
  * @param out Returns a pointer to an inode. Can be released with a
- *            single free() call.
+ *            single @ref sqfs_free call.
  *
  * @return Zero on success, an @ref SQFS_ERROR value on failure.
  */
diff --git a/include/sqfs/predef.h b/include/sqfs/predef.h
index 55ccc86..3254186 100644
--- a/include/sqfs/predef.h
+++ b/include/sqfs/predef.h
@@ -156,4 +156,47 @@ static SQFS_INLINE void *sqfs_copy(const void *obj)
 	return NULL;
 }
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief Free a block of memory created by the squashfs library
+ *
+ * Some objects in the squashfs library allocate temporary chunks of memory
+ * and return pointers to them, with the expectation that the user takes care
+ * of freeing them memory again.
+ *
+ * In the past this worked without hitches, because most sane operating systems
+ * have a single, global C library that everything is linked against. On
+ * systems like Windows that have a huge fragmentation of runtime libraries
+ * this fails, because the library and application can easily end up linked
+ * against different runtime libraries, making it impossible for the program
+ * to free the memory.
+ *
+ * To re-create the same situation on Linux, one would have to link a program
+ * and the squashfs library dynamically, while linking at least one of them
+ * statically against the C runtime, or intentionally linking against two
+ * different runtimes.
+ *
+ * This function mitigates the arising problem by exposing the free() function
+ * of the libraries runtime to the application, so it can propperly release
+ * memory again to the correct heap.
+ *
+ * To maintain API/ABI compatibillity, it is guaranteed that for all versions
+ * of libsquashfs with major version 1, this function does nothing other than
+ * call free() on the C runtime that the library was linked against. If just
+ * calling free() works on your system, it will continue to work, and older
+ * application will continue to work with newer versions of libsquashfs 1.x.
+ * Going forward, new applications using libsquashfs should use this function
+ * instead for better portabillity.
+ *
+ * @param ptr A pointer to a memory block returned by a libsquashfs function.
+ */
+SQFS_API void sqfs_free(void *ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
 #endif /* SQFS_PREDEF_H */
diff --git a/include/sqfs/xattr_reader.h b/include/sqfs/xattr_reader.h
index 8294bd8..a0242fb 100644
--- a/include/sqfs/xattr_reader.h
+++ b/include/sqfs/xattr_reader.h
@@ -160,7 +160,7 @@ SQFS_API int sqfs_xattr_reader_seek_kv(sqfs_xattr_reader_t *xr,
  *
  * @param xr A pointer to an xattr reader instance
  * @param key_out Used to return the decoded key. The underlying memory can be
- *                released using a single free() call.
+ *                released using a single @ref sqfs_free call.
  *
  * @return Zero on success, a negative @ref SQFS_ERROR value on failure.
  */
@@ -181,7 +181,7 @@ int sqfs_xattr_reader_read_key(sqfs_xattr_reader_t *xr,
  * @param xr A pointer to an xattr reader instance.
  * @param key A pointer to the decoded key object.
  * @param val_out Used to return the decoded value. The underlying memory can
- *                be released using a single free() call.
+ *                be released using a single @ref sqfs_free call.
  *
  * @return Zero on success, a negative @ref SQFS_ERROR value on failure.
  */
diff --git a/lib/sqfs/Makemodule.am b/lib/sqfs/Makemodule.am
index fedb7a8..99f06e9 100644
--- a/lib/sqfs/Makemodule.am
+++ b/lib/sqfs/Makemodule.am
@@ -29,6 +29,7 @@ libsquashfs_la_SOURCES += lib/sqfs/block_processor/common.c
 libsquashfs_la_SOURCES += lib/sqfs/block_processor/frontend.c
 libsquashfs_la_SOURCES += lib/sqfs/frag_table.c include/sqfs/frag_table.h
 libsquashfs_la_SOURCES += lib/sqfs/block_writer.c include/sqfs/block_writer.h
+libsquashfs_la_SOURCES += lib/sqfs/misc.c
 libsquashfs_la_CPPFLAGS = $(AM_CPPFLAGS)
 libsquashfs_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(LIBSQUASHFS_SO_VERSION)
 libsquashfs_la_CFLAGS = $(AM_CFLAGS) $(PTHREAD_CFLAGS) $(ZLIB_CFLAGS)
diff --git a/lib/sqfs/misc.c b/lib/sqfs/misc.c
new file mode 100644
index 0000000..74a4203
--- /dev/null
+++ b/lib/sqfs/misc.c
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: LGPL-3.0-or-later */
+/*
+ * misc.c
+ *
+ * Copyright (C) 2021 David Oberhollenzer <goliath@infraroot.at>
+ */
+#define SQFS_BUILDING_DLL
+#include "config.h"
+
+#include "sqfs/predef.h"
+
+#include <stdlib.h>
+
+void sqfs_free(void *ptr)
+{
+	free(ptr);
+}
-- 
2.31.1

