diff --git a/third_party/libdmg-hfsplus/src/common/sizedbuf.c b/third_party/libdmg-hfsplus/src/common/sizedbuf.c new file mode 100644 index 0000000000000..b2de906a7f988 --- /dev/null +++ b/third_party/libdmg-hfsplus/src/common/sizedbuf.c @@ -0,0 +1,36 @@ +#include "sizedbuf.h" + +#include +#include +#include + +#include "common.h" + +SizedBuf* ZAllocBuf(size_t len) { + SizedBuf* ret = calloc(sizeof(SizedBuf) + len, 1); + ASSERT(ret, "ZAllocBuf OOM"); + ret->len = ret->cap = len; + return ret; +} + +SizedBuf* AllocBufCopy(const SizedBuf* buf) { + ASSERT(buf, "AllocBufCopy can't copy out of NULL!"); + ASSERT(buf->len <= buf->cap, "AllocBufCopy observed corrupt input buffer"); + return AllocBufCopyBytes(buf->data, buf->len); +} + +SizedBuf* AllocBufCopyBytes(const char* data, size_t len) { + ASSERT(data, "AllocBufCopyBytes can't copy out of NULL!"); + SizedBuf* ret = malloc(sizeof(SizedBuf) + len); + ASSERT(ret, "AllocBufCopyBytes OOM"); + ret->len = ret->cap = len; + memcpy(ret->data, data, len); + return ret; +} + +SizedBuf* AllocBufCopyString(const char* str) { + ASSERT(str, "AllocBufCopyString can't copy out of NULL!"); + SizedBuf* ret = AllocBufCopyBytes(str, strlen(str) + 1); + ret->len -= 1; + return ret; +} diff --git a/third_party/libdmg-hfsplus/src/dmg/attribution.c b/third_party/libdmg-hfsplus/src/dmg/attribution.c index cf2ab508401f3..a6f5fac18ea79 100644 --- a/third_party/libdmg-hfsplus/src/dmg/attribution.c +++ b/third_party/libdmg-hfsplus/src/dmg/attribution.c @@ -1,12 +1,14 @@ +#include #include #include // For crc32_combine. #include "abstractfile.h" #include "common.h" #include #include +#include "sizedbuf.h" typedef struct AttributionPreservingSentinelData { - char* sentinel; + SizedBuf* sentinelBuf; ChecksumToken beforeRaw_UncompressedChkToken; ChecksumToken raw_UncompressedChkToken; @@ -25,26 +27,10 @@ typedef struct AttributionPreservingSentinelData { int64_t afterRaw_CompressedLength; } AttributionPreservingSentinelData; -char const * -FindStrInBuf(char const * buf, size_t bufLen, char const * str) -{ - size_t index = 0; - while (index < bufLen) { - char const * result = strstr(buf + index, str); - if (result) { - return result; - } - while ((buf[index] != '\0') && (index < bufLen)) { - index++; - } - index++; - } - return NULL; -} - enum ShouldKeepRaw sentinelShouldKeepRaw(AbstractAttribution* attribution, const void* data, size_t len, const void* nextData, size_t nextLen) { AttributionPreservingSentinelData* attributionData = (AttributionPreservingSentinelData*)attribution->data; - if (NULL != FindStrInBuf((char const*)data, len, (const char*)attributionData->sentinel)) { + const SizedBuf* sentinelBuf = attributionData->sentinelBuf; + if (NULL != memmem((const char*)data, len, sentinelBuf->data, sentinelBuf->len)) { return KeepCurrentRaw; } @@ -53,7 +39,7 @@ enum ShouldKeepRaw sentinelShouldKeepRaw(AbstractAttribution* attribution, const char* combinedData = malloc(len + nextLen); memcpy(combinedData, data, len); memcpy(combinedData + len, nextData, nextLen); - if (NULL != FindStrInBuf((char const*)combinedData, len + nextLen, (const char*)attributionData->sentinel)) { + if (NULL != memmem((const char*)combinedData, len + nextLen, sentinelBuf->data, sentinelBuf->len)) { return KeepCurrentAndNextRaw; } } @@ -183,13 +169,17 @@ void sentinelAfterMainBlkx(AbstractAttribution* attribution, AbstractFile* abstr } AbstractAttribution* createAbstractAttributionPreservingSentinel(const char* sentinel) { - AbstractAttribution* attribution; - attribution = (AbstractAttribution*) malloc(sizeof(AbstractAttribution)); + SizedBuf* sentinelBuf = AllocBufCopyString(sentinel); + AbstractAttribution* ret = createAbstractAttributionPreservingSentinelBuf(sentinelBuf); + free(sentinelBuf); + return ret; +} - AttributionPreservingSentinelData* data = malloc(sizeof(AttributionPreservingSentinelData)); - memset(data, 0, sizeof(AttributionPreservingSentinelData)); - data->sentinel = malloc(strlen(sentinel) + 1); - strcpy(data->sentinel, sentinel); +AbstractAttribution* createAbstractAttributionPreservingSentinelBuf(const SizedBuf* sentinelBuf) { + AbstractAttribution* attribution = calloc(1, sizeof(AbstractAttribution)); + + AttributionPreservingSentinelData* data = calloc(1, sizeof(AttributionPreservingSentinelData)); + data->sentinelBuf = AllocBufCopy(sentinelBuf); data->raw_UncompressedLength = -1; data->afterRaw_UncompressedLength = -1; data->raw_CompressedLength = -1; @@ -205,8 +195,16 @@ AbstractAttribution* createAbstractAttributionPreservingSentinel(const char* sen uint32_t calculateMasterChecksum(ResourceKey* resources); -int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const char* anchor, const char* data, size_t dataLen) -{ +int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const char* anchor, const char* data, size_t dataLen) { + SizedBuf* sentinelBuf = AllocBufCopyString(anchor); + SizedBuf* dataBuf = AllocBufCopyBytes(data, dataLen); + int ret = updateAttributionFromBufs(abstractIn, abstractOut, sentinelBuf, dataBuf); + free(dataBuf); + free(sentinelBuf); + return ret; +} + +int updateAttributionFromBufs(AbstractFile* abstractIn, AbstractFile* abstractOut, const SizedBuf* sentinelBuf, const SizedBuf* dataBuf) { // In an `attributable` DMG file: // - read `attribution` resource // - update bytes in BZ_RAW block in place @@ -218,6 +216,7 @@ int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const // - update data fork checksum (compressed) // - update master checksum (uncompressed) + ASSERT(sentinelBuf->len >= dataBuf->len, "data too long!"); off_t fileLength; UDIFResourceFile resourceFile; @@ -227,9 +226,7 @@ int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const abstractIn->seek(abstractIn, fileLength - sizeof(UDIFResourceFile)); readUDIFResourceFile(abstractIn, &resourceFile); - char* resourceXML; - - resourceXML = malloc(resourceFile.fUDIFXMLLength + 1); + char* resourceXML = calloc(resourceFile.fUDIFXMLLength + 1, 1); ASSERT( abstractIn->seek(abstractIn, (off_t)(resourceFile.fUDIFXMLOffset)) == 0, "fseeko" ); ASSERT( abstractIn->read(abstractIn, resourceXML, (size_t)resourceFile.fUDIFXMLLength) == (size_t)resourceFile.fUDIFXMLLength, "fread" ); resourceXML[resourceFile.fUDIFXMLLength] = 0; @@ -237,7 +234,6 @@ int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const ResourceKey* resources; resources = readResources(resourceXML, resourceFile.fUDIFXMLLength, true); ResourceKey* resource = getResourceByKey(resources, "plst"); - unsigned char* mine = (unsigned char*)(resource->data->name); AttributionResource* attributionResource = (AttributionResource*)(resource->data->name); ASSERT(attributionResource->signature == ATTR_SIGNATURE, "bad attr signature!"); @@ -249,7 +245,6 @@ int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const // Step 1. Replace bytes at anchor. ASSERT(abstractIn->seek(abstractIn, 0) == 0, "seek in"); - size_t inLength = abstractIn->getLength(abstractIn); while (1) { unsigned char buffer[8192]; size_t readLength = abstractIn->read(abstractIn, buffer, 8192); @@ -260,37 +255,30 @@ int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const } ASSERT(abstractIn->seek(abstractIn, attributionResource->rawPos) == 0, "seek in"); - char* rawBuffer = malloc(attributionResource->rawLength); - ASSERT(rawBuffer, "malloc rawBuffer"); - ASSERT(abstractIn->read(abstractIn, rawBuffer, attributionResource->rawLength) == attributionResource->rawLength, "read raw in"); + SizedBuf* rawBuf = ZAllocBuf(attributionResource->rawLength); + ASSERT(abstractIn->read(abstractIn, rawBuf->data, rawBuf->len) == rawBuf->len, "read raw in"); - printf("Looking for anchor: '%s'\n", anchor); + printf("Looking for anchor.\n"); - const char* rawAnchor = FindStrInBuf((const char*)rawBuffer, attributionResource->rawLength, anchor); - ASSERT(rawAnchor, "anchor position"); + char* rawAnchor = memmem(rawBuf->data, rawBuf->len, sentinelBuf->data, sentinelBuf->len); + ASSERT(rawAnchor, "cannot find sentinel when replacing attribution"); - int64_t anchorOffset = rawAnchor - rawBuffer; - printf("anchorOffset: 0x%llx\n", anchorOffset); - - ASSERT(rawAnchor + dataLen <= rawBuffer + attributionResource->rawLength, "data too long!"); - // Zero out the anchor area, in case the data is shorter than the anchor - // Note that we're taking the strlen of `rawAnchor` and not the `anchor` - // passed in. This ensures that the entire anchor is zero'ed, even if only - // a prefix of it was provided. - memset((void *)rawAnchor, 0, strlen(rawAnchor)); - // Copy the new data into the same spot the anchor was in - memcpy((void *)rawAnchor, data, dataLen); + // Zero out the anchor area, in case the data is shorter than the anchor. + // We found the entire anchor in this buffer, so we know this memset "fits". + memset(rawAnchor, 0, sentinelBuf->len); + // Copy the new data into the same spot the anchor was in. + memcpy(rawAnchor, dataBuf->data, dataBuf->len); // Write the new block. ASSERT(abstractOut->seek(abstractOut, attributionResource->rawPos) == 0, "seek out"); - ASSERT(abstractOut->write(abstractOut, rawBuffer, attributionResource->rawLength) == attributionResource->rawLength, "write data"); + ASSERT(abstractOut->write(abstractOut, rawBuf->data, rawBuf->len) == attributionResource->rawLength, "write data"); // New block checksum. ChecksumToken newRawToken; memset(&newRawToken, 0, sizeof(ChecksumToken)); - CRCProxy(&newRawToken, (unsigned char*)rawBuffer, attributionResource->rawLength); + CRCProxy(&newRawToken, (unsigned char*)rawBuf->data, rawBuf->len); - free(rawBuffer); + free(rawBuf); // Step 2: update "attribution" resource. attributionResource->rawChecksum = newRawToken.crc; diff --git a/third_party/libdmg-hfsplus/src/dmg/dmg.c b/third_party/libdmg-hfsplus/src/dmg/dmg.c index 68c651c4bc60b..411d8df2734e3 100644 --- a/third_party/libdmg-hfsplus/src/dmg/dmg.c +++ b/third_party/libdmg-hfsplus/src/dmg/dmg.c @@ -4,8 +4,9 @@ #include #include -#include -#include +#include "dmg/dmg.h" +#include "dmg/filevault.h" +#include "sizedbuf.h" char endianness; @@ -128,14 +129,15 @@ int main(int argc, char* argv[]) { } else if(strcmp(cmd, "dmg") == 0) { convertToDMG(in, out, &comp, runSectors); } else if(strcmp(cmd, "attribute") == 0) { - char *anchor, *data; if(argc < optind + 2) { printf("Not enough arguments: attribute "); return 2; } - anchor = argv[optind++]; - data = argv[optind++]; - updateAttribution(in, out, anchor, data, strlen(data)); + SizedBuf* anchorBuf = AllocBufCopyString(argv[optind++]); + SizedBuf* dataBuf = AllocBufCopyString(argv[optind++]); + updateAttributionFromBufs(in, out, anchorBuf, dataBuf); + free(dataBuf); + free(anchorBuf); } return 0; diff --git a/third_party/libdmg-hfsplus/src/includes/dmg/attribution.h b/third_party/libdmg-hfsplus/src/includes/dmg/attribution.h index a84509cd8b869..163e2cc0a0772 100644 --- a/third_party/libdmg-hfsplus/src/includes/dmg/attribution.h +++ b/third_party/libdmg-hfsplus/src/includes/dmg/attribution.h @@ -3,6 +3,7 @@ #include "abstractfile.h" #include "common.h" +#include "sizedbuf.h" #include #define ATTR_SIGNATURE 0x61747472 // 'attr' @@ -112,9 +113,21 @@ extern "C" { // than one instance, of the given sentinel is found, attribution will fail. AbstractAttribution* createAbstractAttributionPreservingSentinel(const char* sentinel); + // Return an `AbstractAttribution` structure (instance) that will preserve the + // *unique* instance of the given string sentinel. If no instance, or more + // than one instance, of the given sentinel is found, attribution will fail. + AbstractAttribution* createAbstractAttributionPreservingSentinelBuf(const SizedBuf* sentinel); + // Copy an "attributable" DMG -- one structured for attribution -- to the // given output, write `len` of the given `bytes` over the `sentinel`. int updateAttribution(AbstractFile* abstractIn, AbstractFile* abstractOut, const char* sentinel, const char* bytes, size_t len); + + // Copy an "attributable" DMG -- one structured for attribution -- to the + // given output, writing the given `dataBuf` over the part of the data where + // `sentinelBuf->data` (up to its `len`) is found. If `dataBuf` is shorter + // than `sentinelBuf`, the remaining space in the sentinel is overwritten with + // zeroes. If `dataBuf` is longer, the program crashes. + int updateAttributionFromBufs(AbstractFile* abstractIn, AbstractFile* abstractOut, const SizedBuf* sentinelBuf, const SizedBuf* dataBuf); #ifdef __cplusplus } #endif diff --git a/third_party/libdmg-hfsplus/src/includes/sizedbuf.h b/third_party/libdmg-hfsplus/src/includes/sizedbuf.h new file mode 100644 index 0000000000000..2d6a770d150f5 --- /dev/null +++ b/third_party/libdmg-hfsplus/src/includes/sizedbuf.h @@ -0,0 +1,46 @@ +#ifndef _LIBDMG_HFSPLUS_SIZEDBUF_H +#define _LIBDMG_HFSPLUS_SIZEDBUF_H + +#include + +// SizedBuf is an arbitrary-length buffer for arbitrary data. SizedBuf +// instances can be allocated with AllocBuf, ZAllocBuf, AllocCopyBuf, or +// AllocCopyBytes. Free them with `free`. +typedef struct { + // Length of valid data in `data` (usage-specific). + size_t len; + + // Allocated length of `data`. + size_t cap; + + // Arbitrary data storage; allocated length is `cap`. + char data[]; +} SizedBuf; + +#ifdef __cplusplus +extern "C" { +#endif + +// ZAllocBuf allocates a new SizedBuf with capacity and len both `len`. +// It is filled with zeroes. +SizedBuf* ZAllocBuf(size_t len); + +// AllocBufCopy allocates a new SizedBuf with data copied from the provided +// buffer up to its `len`. The new SizedBuf has `cap` at least equal to the +// `len` of the provided `buf` (not necessarily up to its `cap`). +SizedBuf* AllocBufCopy(const SizedBuf* buf); + +// AllocBufCopyBytes allocates a new SizedBuf with data copied from the provided +// byte buffer. +SizedBuf* AllocBufCopyBytes(const char* data, size_t len); + +// AllocBufCopyString allocates a new SizedBuf with data copied from the +// provided NUL-terminated C string. The `len` of the buffer excludes the +// NUL terminator, but the buffer's `cap` includes it. +SizedBuf* AllocBufCopyString(const char* str); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // _LIBDMG_HFSPLUS_SIZEDBUF_H \ No newline at end of file