Merge pull request #2419 from felixhandte/asan-dont-poison-static-allocs

Don't ASAN-Poison Statically-Allocated Workspaces
This commit is contained in:
Felix Handte
2020-12-09 16:50:52 -05:00
committed by GitHub
2 changed files with 48 additions and 22 deletions
+14 -14
View File
@@ -109,7 +109,7 @@ ZSTD_CCtx* ZSTD_initStaticCCtx(void* workspace, size_t workspaceSize)
ZSTD_CCtx* cctx; ZSTD_CCtx* cctx;
if (workspaceSize <= sizeof(ZSTD_CCtx)) return NULL; /* minimum size */ if (workspaceSize <= sizeof(ZSTD_CCtx)) return NULL; /* minimum size */
if ((size_t)workspace & 7) return NULL; /* must be 8-aligned */ if ((size_t)workspace & 7) return NULL; /* must be 8-aligned */
ZSTD_cwksp_init(&ws, workspace, workspaceSize); ZSTD_cwksp_init(&ws, workspace, workspaceSize, ZSTD_cwksp_static_alloc);
cctx = (ZSTD_CCtx*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CCtx)); cctx = (ZSTD_CCtx*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CCtx));
if (cctx == NULL) return NULL; if (cctx == NULL) return NULL;
@@ -457,12 +457,12 @@ ZSTD_bounds ZSTD_cParam_getBounds(ZSTD_cParameter param)
bounds.lowerBound = (int)ZSTD_bm_buffered; bounds.lowerBound = (int)ZSTD_bm_buffered;
bounds.upperBound = (int)ZSTD_bm_stable; bounds.upperBound = (int)ZSTD_bm_stable;
return bounds; return bounds;
case ZSTD_c_blockDelimiters: case ZSTD_c_blockDelimiters:
bounds.lowerBound = (int)ZSTD_sf_noBlockDelimiters; bounds.lowerBound = (int)ZSTD_sf_noBlockDelimiters;
bounds.upperBound = (int)ZSTD_sf_explicitBlockDelimiters; bounds.upperBound = (int)ZSTD_sf_explicitBlockDelimiters;
return bounds; return bounds;
case ZSTD_c_validateSequences: case ZSTD_c_validateSequences:
bounds.lowerBound = 0; bounds.lowerBound = 0;
bounds.upperBound = 1; bounds.upperBound = 1;
@@ -781,12 +781,12 @@ size_t ZSTD_CCtxParams_setParameter(ZSTD_CCtx_params* CCtxParams,
BOUNDCHECK(ZSTD_c_stableOutBuffer, value); BOUNDCHECK(ZSTD_c_stableOutBuffer, value);
CCtxParams->outBufferMode = (ZSTD_bufferMode_e)value; CCtxParams->outBufferMode = (ZSTD_bufferMode_e)value;
return CCtxParams->outBufferMode; return CCtxParams->outBufferMode;
case ZSTD_c_blockDelimiters: case ZSTD_c_blockDelimiters:
BOUNDCHECK(ZSTD_c_blockDelimiters, value); BOUNDCHECK(ZSTD_c_blockDelimiters, value);
CCtxParams->blockDelimiters = (ZSTD_sequenceFormat_e)value; CCtxParams->blockDelimiters = (ZSTD_sequenceFormat_e)value;
return CCtxParams->blockDelimiters; return CCtxParams->blockDelimiters;
case ZSTD_c_validateSequences: case ZSTD_c_validateSequences:
BOUNDCHECK(ZSTD_c_validateSequences, value); BOUNDCHECK(ZSTD_c_validateSequences, value);
CCtxParams->validateSequences = value; CCtxParams->validateSequences = value;
@@ -3692,7 +3692,7 @@ static ZSTD_CDict* ZSTD_createCDict_advanced_internal(size_t dictSize,
return NULL; return NULL;
} }
ZSTD_cwksp_init(&ws, workspace, workspaceSize); ZSTD_cwksp_init(&ws, workspace, workspaceSize, ZSTD_cwksp_dynamic_alloc);
cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict)); cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict));
assert(cdict != NULL); assert(cdict != NULL);
@@ -3836,7 +3836,7 @@ const ZSTD_CDict* ZSTD_initStaticCDict(
{ {
ZSTD_cwksp ws; ZSTD_cwksp ws;
ZSTD_cwksp_init(&ws, workspace, workspaceSize); ZSTD_cwksp_init(&ws, workspace, workspaceSize, ZSTD_cwksp_static_alloc);
cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict)); cdict = (ZSTD_CDict*)ZSTD_cwksp_reserve_object(&ws, sizeof(ZSTD_CDict));
if (cdict == NULL) return NULL; if (cdict == NULL) return NULL;
ZSTD_cwksp_move(&cdict->workspace, &ws); ZSTD_cwksp_move(&cdict->workspace, &ws);
@@ -4635,10 +4635,10 @@ static size_t ZSTD_copySequencesToSeqStoreExplicitBlockDelim(ZSTD_CCtx* cctx, ZS
/* Returns the number of bytes to move the current read position back by. Only non-zero /* Returns the number of bytes to move the current read position back by. Only non-zero
* if we ended up splitting a sequence. Otherwise, it may return a ZSTD error if something * if we ended up splitting a sequence. Otherwise, it may return a ZSTD error if something
* went wrong. * went wrong.
* *
* This function will attempt to scan through blockSize bytes represented by the sequences * This function will attempt to scan through blockSize bytes represented by the sequences
* in inSeqs, storing any (partial) sequences. * in inSeqs, storing any (partial) sequences.
* *
* Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to * Occasionally, we may want to change the actual number of bytes we consumed from inSeqs to
* avoid splitting a match, or to avoid splitting a match such that it would produce a match * avoid splitting a match, or to avoid splitting a match such that it would produce a match
* smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block. * smaller than MINMATCH. In this case, we return the number of bytes that we didn't read from this block.
@@ -4659,7 +4659,7 @@ static size_t ZSTD_copySequencesToSeqStoreNoBlockDelim(ZSTD_CCtx* cctx, ZSTD_seq
U32 matchLength; U32 matchLength;
U32 rawOffset; U32 rawOffset;
U32 offCode; U32 offCode;
if (cctx->cdict) { if (cctx->cdict) {
dictSize = cctx->cdict->dictContentSize; dictSize = cctx->cdict->dictContentSize;
} else if (cctx->prefixDict.dict) { } else if (cctx->prefixDict.dict) {
@@ -4793,7 +4793,7 @@ static size_t ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
size_t compressedSeqsSize; size_t compressedSeqsSize;
size_t remaining = srcSize; size_t remaining = srcSize;
ZSTD_sequencePosition seqPos = {0, 0, 0}; ZSTD_sequencePosition seqPos = {0, 0, 0};
BYTE const* ip = (BYTE const*)src; BYTE const* ip = (BYTE const*)src;
BYTE* op = (BYTE*)dst; BYTE* op = (BYTE*)dst;
ZSTD_sequenceCopier sequenceCopier = ZSTD_selectSequenceCopier(cctx->appliedParams.blockDelimiters); ZSTD_sequenceCopier sequenceCopier = ZSTD_selectSequenceCopier(cctx->appliedParams.blockDelimiters);
@@ -4879,7 +4879,7 @@ static size_t ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
cSize += cBlockSize; cSize += cBlockSize;
DEBUGLOG(4, "cSize running total: %zu", cSize); DEBUGLOG(4, "cSize running total: %zu", cSize);
if (lastBlock) { if (lastBlock) {
break; break;
} else { } else {
@@ -4890,7 +4890,7 @@ static size_t ZSTD_compressSequences_internal(ZSTD_CCtx* cctx,
cctx->isFirstBlock = 0; cctx->isFirstBlock = 0;
} }
} }
return cSize; return cSize;
} }
+34 -8
View File
@@ -44,6 +44,16 @@ typedef enum {
ZSTD_cwksp_alloc_aligned ZSTD_cwksp_alloc_aligned
} ZSTD_cwksp_alloc_phase_e; } ZSTD_cwksp_alloc_phase_e;
/**
* Used to describe whether the workspace is statically allocated (and will not
* necessarily ever be freed), or if it's dynamically allocated and we can
* expect a well-formed caller to free this.
*/
typedef enum {
ZSTD_cwksp_dynamic_alloc,
ZSTD_cwksp_static_alloc
} ZSTD_cwksp_static_alloc_e;
/** /**
* Zstd fits all its internal datastructures into a single continuous buffer, * Zstd fits all its internal datastructures into a single continuous buffer,
* so that it only needs to perform a single OS allocation (or so that a buffer * so that it only needs to perform a single OS allocation (or so that a buffer
@@ -137,9 +147,10 @@ typedef struct {
void* tableValidEnd; void* tableValidEnd;
void* allocStart; void* allocStart;
int allocFailed; BYTE allocFailed;
int workspaceOversizedDuration; int workspaceOversizedDuration;
ZSTD_cwksp_alloc_phase_e phase; ZSTD_cwksp_alloc_phase_e phase;
ZSTD_cwksp_static_alloc_e isStatic;
} ZSTD_cwksp; } ZSTD_cwksp;
/*-************************************* /*-*************************************
@@ -256,7 +267,9 @@ MEM_STATIC void* ZSTD_cwksp_reserve_internal(
/* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on
* either size. */ * either size. */
alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE; alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;
__asan_unpoison_memory_region(alloc, bytes); if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
__asan_unpoison_memory_region(alloc, bytes);
}
#endif #endif
return alloc; return alloc;
@@ -302,7 +315,9 @@ MEM_STATIC void* ZSTD_cwksp_reserve_table(ZSTD_cwksp* ws, size_t bytes) {
ws->tableEnd = end; ws->tableEnd = end;
#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) #if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
__asan_unpoison_memory_region(alloc, bytes); if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
__asan_unpoison_memory_region(alloc, bytes);
}
#endif #endif
return alloc; return alloc;
@@ -341,7 +356,9 @@ MEM_STATIC void* ZSTD_cwksp_reserve_object(ZSTD_cwksp* ws, size_t bytes) {
/* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on /* Move alloc so there's ZSTD_CWKSP_ASAN_REDZONE_SIZE unused space on
* either size. */ * either size. */
alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE; alloc = (BYTE *)alloc + ZSTD_CWKSP_ASAN_REDZONE_SIZE;
__asan_unpoison_memory_region(alloc, bytes); if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
__asan_unpoison_memory_region(alloc, bytes);
}
#endif #endif
return alloc; return alloc;
@@ -398,7 +415,11 @@ MEM_STATIC void ZSTD_cwksp_clear_tables(ZSTD_cwksp* ws) {
DEBUGLOG(4, "cwksp: clearing tables!"); DEBUGLOG(4, "cwksp: clearing tables!");
#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) #if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
{ /* We don't do this when the workspace is statically allocated, because
* when that is the case, we have no capability to hook into the end of the
* workspace's lifecycle to unpoison the memory.
*/
if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd; size_t size = (BYTE*)ws->tableValidEnd - (BYTE*)ws->objectEnd;
__asan_poison_memory_region(ws->objectEnd, size); __asan_poison_memory_region(ws->objectEnd, size);
} }
@@ -427,7 +448,11 @@ MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) {
#endif #endif
#if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE) #if ZSTD_ADDRESS_SANITIZER && !defined (ZSTD_ASAN_DONT_POISON_WORKSPACE)
{ /* We don't do this when the workspace is statically allocated, because
* when that is the case, we have no capability to hook into the end of the
* workspace's lifecycle to unpoison the memory.
*/
if (ws->isStatic == ZSTD_cwksp_dynamic_alloc) {
size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->objectEnd; size_t size = (BYTE*)ws->workspaceEnd - (BYTE*)ws->objectEnd;
__asan_poison_memory_region(ws->objectEnd, size); __asan_poison_memory_region(ws->objectEnd, size);
} }
@@ -447,7 +472,7 @@ MEM_STATIC void ZSTD_cwksp_clear(ZSTD_cwksp* ws) {
* Any existing values in the workspace are ignored (the previously managed * Any existing values in the workspace are ignored (the previously managed
* buffer, if present, must be separately freed). * buffer, if present, must be separately freed).
*/ */
MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size) { MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size, ZSTD_cwksp_static_alloc_e isStatic) {
DEBUGLOG(4, "cwksp: init'ing workspace with %zd bytes", size); DEBUGLOG(4, "cwksp: init'ing workspace with %zd bytes", size);
assert(((size_t)start & (sizeof(void*)-1)) == 0); /* ensure correct alignment */ assert(((size_t)start & (sizeof(void*)-1)) == 0); /* ensure correct alignment */
ws->workspace = start; ws->workspace = start;
@@ -455,6 +480,7 @@ MEM_STATIC void ZSTD_cwksp_init(ZSTD_cwksp* ws, void* start, size_t size) {
ws->objectEnd = ws->workspace; ws->objectEnd = ws->workspace;
ws->tableValidEnd = ws->objectEnd; ws->tableValidEnd = ws->objectEnd;
ws->phase = ZSTD_cwksp_alloc_objects; ws->phase = ZSTD_cwksp_alloc_objects;
ws->isStatic = isStatic;
ZSTD_cwksp_clear(ws); ZSTD_cwksp_clear(ws);
ws->workspaceOversizedDuration = 0; ws->workspaceOversizedDuration = 0;
ZSTD_cwksp_assert_internal_consistency(ws); ZSTD_cwksp_assert_internal_consistency(ws);
@@ -464,7 +490,7 @@ MEM_STATIC size_t ZSTD_cwksp_create(ZSTD_cwksp* ws, size_t size, ZSTD_customMem
void* workspace = ZSTD_customMalloc(size, customMem); void* workspace = ZSTD_customMalloc(size, customMem);
DEBUGLOG(4, "cwksp: creating new workspace with %zd bytes", size); DEBUGLOG(4, "cwksp: creating new workspace with %zd bytes", size);
RETURN_ERROR_IF(workspace == NULL, memory_allocation, "NULL pointer!"); RETURN_ERROR_IF(workspace == NULL, memory_allocation, "NULL pointer!");
ZSTD_cwksp_init(ws, workspace, size); ZSTD_cwksp_init(ws, workspace, size, ZSTD_cwksp_dynamic_alloc);
return 0; return 0;
} }