mirror of
https://github.com/Kitware/CMake.git
synced 2026-06-24 16:58:07 +00:00
file(ARCHIVE_EXTRACT): Fix resource leaks on error paths
extract_tar() allocates archive_read, archive_write_disk, and archive_match handles, but several early-return error paths did not free them. Most notably, a PATTERNS entry matching nothing leaked all three handles on every call, which accumulates inside a long-running configure. Scope the three handles with std::unique_ptr custom deleters so every return path releases them automatically. std::unique_ptr does not invoke the deleter for a null pointer, and the libarchive *_free functions are themselves no-ops on null, so no explicit guarding is required. archive_read_free()/archive_write_free() implicitly close the handle if needed, matching the previous behavior. Fixes: #27872
This commit is contained in:
committed by
Brad King
parent
cdd5dbbba5
commit
c31361d089
@@ -2503,14 +2503,33 @@ bool copy_data(struct archive* ar, struct archive* aw)
|
||||
# endif
|
||||
}
|
||||
|
||||
struct ArchiveReadDeleter
|
||||
{
|
||||
void operator()(struct archive* a) const { archive_read_free(a); }
|
||||
};
|
||||
|
||||
struct ArchiveWriteDeleter
|
||||
{
|
||||
void operator()(struct archive* a) const { archive_write_free(a); }
|
||||
};
|
||||
|
||||
struct ArchiveMatchDeleter
|
||||
{
|
||||
void operator()(struct archive* a) const { archive_match_free(a); }
|
||||
};
|
||||
|
||||
bool extract_tar(std::string const& arFileName,
|
||||
std::vector<std::string> const& files,
|
||||
std::string const& encoding, bool verbose,
|
||||
cmSystemTools::cmTarExtractTimestamps extractTimestamps,
|
||||
bool extract)
|
||||
{
|
||||
struct archive* a = archive_read_new();
|
||||
struct archive* ext = archive_write_disk_new();
|
||||
std::unique_ptr<struct archive, ArchiveReadDeleter> a_owner(
|
||||
archive_read_new());
|
||||
std::unique_ptr<struct archive, ArchiveWriteDeleter> ext_owner(
|
||||
archive_write_disk_new());
|
||||
struct archive* a = a_owner.get();
|
||||
struct archive* ext = ext_owner.get();
|
||||
if (extract) {
|
||||
int flags =
|
||||
ARCHIVE_EXTRACT_SECURE_NODOTDOT | ARCHIVE_EXTRACT_SECURE_NOABSOLUTEPATHS;
|
||||
@@ -2519,8 +2538,6 @@ bool extract_tar(std::string const& arFileName,
|
||||
}
|
||||
if (archive_write_disk_set_options(ext, flags) != ARCHIVE_OK) {
|
||||
ArchiveError("Problem with archive_write_disk_set_options(): ", ext);
|
||||
archive_write_free(ext);
|
||||
archive_read_free(a);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2537,7 +2554,9 @@ bool extract_tar(std::string const& arFileName,
|
||||
}
|
||||
struct archive_entry* entry;
|
||||
|
||||
struct archive* matching = archive_match_new();
|
||||
std::unique_ptr<struct archive, ArchiveMatchDeleter> matching_owner(
|
||||
archive_match_new());
|
||||
struct archive* matching = matching_owner.get();
|
||||
if (!matching) {
|
||||
cmSystemTools::Error("Out of memory");
|
||||
return false;
|
||||
@@ -2554,10 +2573,6 @@ bool extract_tar(std::string const& arFileName,
|
||||
int r = cm_archive_read_open_filename(a, arFileName.c_str(), 10240);
|
||||
if (r) {
|
||||
ArchiveError("Problem with archive_read_open_filename(): ", a);
|
||||
archive_write_free(ext);
|
||||
archive_read_close(a);
|
||||
archive_read_free(a);
|
||||
archive_match_free(matching);
|
||||
return false;
|
||||
}
|
||||
for (;;) {
|
||||
@@ -2633,10 +2648,6 @@ bool extract_tar(std::string const& arFileName,
|
||||
return false;
|
||||
}
|
||||
}
|
||||
archive_match_free(matching);
|
||||
archive_write_free(ext);
|
||||
archive_read_close(a);
|
||||
archive_read_free(a);
|
||||
return r == ARCHIVE_EOF || r == ARCHIVE_OK;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,6 +70,7 @@ run_cmake(zip-filtered)
|
||||
|
||||
run_cmake(create-missing-args)
|
||||
run_cmake(extract-missing-args)
|
||||
run_cmake(extract-missing-pattern)
|
||||
|
||||
run_cmake(unsupported-format)
|
||||
run_cmake(zip-with-bad-compression)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
1
|
||||
@@ -0,0 +1 @@
|
||||
^CMake Error: tar: no_such_entry: Not found in archive
|
||||
26
Tests/RunCMake/File_Archive/extract-missing-pattern.cmake
Normal file
26
Tests/RunCMake/File_Archive/extract-missing-pattern.cmake
Normal file
@@ -0,0 +1,26 @@
|
||||
# A PATTERNS entry that matches no entry in the archive must fail with a clear
|
||||
# "Not found in archive" diagnostic. This exercises the most user-reachable
|
||||
# error path in extract_tar() (the one that previously leaked all three
|
||||
# libarchive handles), so it guards that the error path still behaves correctly
|
||||
# after the RAII cleanup refactor.
|
||||
|
||||
set(archive "${CMAKE_CURRENT_BINARY_DIR}/test.zip")
|
||||
set(src "${CMAKE_CURRENT_BINARY_DIR}/src")
|
||||
set(dest "${CMAKE_CURRENT_BINARY_DIR}/dest")
|
||||
|
||||
file(REMOVE "${archive}")
|
||||
file(REMOVE_RECURSE "${src}" "${dest}")
|
||||
file(MAKE_DIRECTORY "${src}")
|
||||
file(WRITE "${src}/f1.txt" "content\n")
|
||||
file(MAKE_DIRECTORY "${dest}")
|
||||
|
||||
file(ARCHIVE_CREATE
|
||||
OUTPUT "${archive}"
|
||||
FORMAT zip
|
||||
PATHS "${src}")
|
||||
|
||||
# "no_such_entry" matches nothing in the archive, so extraction must fail.
|
||||
file(ARCHIVE_EXTRACT
|
||||
INPUT "${archive}"
|
||||
DESTINATION "${dest}"
|
||||
PATTERNS no_such_entry)
|
||||
Reference in New Issue
Block a user