diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2023-03-22 12:04:33 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-22 12:04:33 -0400 |
| commit | d4f99c8bac8b28f18c864a717d8833db6a1c872d (patch) | |
| tree | ebea06c019130d8248d5e4f6bccf5e4b2649e3cb /source/slang/slang-ir-obfuscate-loc.cpp | |
| parent | d8a40abba5223fbcb56c52b04ccb88c02bbaf79f (diff) | |
Source map obfuscation (#2717)
* #include an absolute path didn't work - because paths were taken to always be relative.
* WIP source map.
* Split out handling of RttiTypeFuncs to a map type.
* Make RttiTypeFuncsMap hold default impls.
* Slightly more sophisticated RttiTypeFuncsMap
* Source map decoding.
* Fix tabs.
* Fix asserts due to negative values.
* Use less obscure mechanisms in SourceMap.
* Source map decoding.
Simplifying SourceMap usage.
* First attempt at ouputting a source map as part of emit.
* Added support for -source-map option. SourceMap is added to the artifact.
* Small improvements around column calculation in SourceWriter.
* Source Loc obuscation WIP.
* Fix some issues around SourceMap obfuscation.
* Split out obfuscation into its own file.
* Keep obfuscated SourceMap even through serialization bottleneck.
Diffstat (limited to 'source/slang/slang-ir-obfuscate-loc.cpp')
| -rw-r--r-- | source/slang/slang-ir-obfuscate-loc.cpp | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/source/slang/slang-ir-obfuscate-loc.cpp b/source/slang/slang-ir-obfuscate-loc.cpp new file mode 100644 index 000000000..b3f5d5cd3 --- /dev/null +++ b/source/slang/slang-ir-obfuscate-loc.cpp @@ -0,0 +1,279 @@ +// slang-ir-obfuscate-loc.cpp +#include "slang-ir-obfuscate-loc.h" + +#include "../../slang.h" + +#include "../core/slang-random-generator.h" +#include "../core/slang-hash.h" +#include "../core/slang-char-util.h" + +namespace Slang +{ + +namespace { // anonymous + +struct InstWithLoc +{ + typedef InstWithLoc ThisType; + + SLANG_FORCE_INLINE bool operator<(const ThisType& rhs) const { return loc.getRaw() < rhs.loc.getRaw(); } + + IRInst* inst; + SourceLoc loc; +}; + +struct LocPair +{ + SourceLoc originalLoc; + SourceLoc obfuscatedLoc; +}; + +} // anonymous + +static void _findInstsRec(IRInst* inst, List<InstWithLoc>& out) +{ + if (inst->sourceLoc.isValid()) + { + InstWithLoc instWithLoc; + instWithLoc.inst = inst; + instWithLoc.loc = inst->sourceLoc; + out.add(instWithLoc); + } + + for (IRInst* child : inst->getModifiableChildren()) + { + _findInstsRec(child, out); + } +} + +SlangResult obfuscateModuleLocs(IRModule* module, SourceManager* sourceManager) +{ + // There shouldn't be an obfuscated source map set + SLANG_ASSERT(module->getObfuscatedSourceMap() == nullptr); + + List<InstWithLoc> instWithLocs; + + // Find all of the instructions with source locs + _findInstsRec(module->getModuleInst(), instWithLocs); + + // Sort them + instWithLocs.sort(); + + // Lets produce a hash, so we can use as a key for random number generation. + // We could base it on time, or some other thing as there is no requirement for + // stability or consistency. + // We use a hash because it avoids issues around clocks, and availability of a clock + // as a good source of entropy. + // + // An argument *could* be made to generate the name via some mechanism that uniquely identified the + // combination of flags, options, files, names that identified the compilation, but that is + // not easily achieved. + HashCode hash = 0; + + List<LocPair> locPairs; + + { + SourceLoc curLoc; + for (const auto& instWithLoc : instWithLocs) + { + hash = combineHash(hash, getHashCode(instWithLoc.inst)); + hash = combineHash(hash, getHashCode(instWithLoc.loc.getRaw())); + + if (instWithLoc.loc != curLoc) + { + LocPair locPair; + locPair.originalLoc = instWithLoc.loc; + locPairs.add(locPair); + + // This is the current loc + curLoc = instWithLoc.loc; + } + } + } + + const Count uniqueLocCount = locPairs.getCount(); + + // We need a seed to make this random on each run + const uint32_t randomSeed = uint32_t(hash); + RefPtr<RandomGenerator> rand = RandomGenerator::create(randomSeed); + + // We want a random unique name because we could have multiple obfuscated modules + // and we need to identify each + + PathInfo obfusctatedPathInfo; + + { + // We need a pathInfo to *identify* this modules obfuscated locs. + // We are going to use a random number, seeded from the hash to do this. + // Turning the number as hex as the name. + { + StringBuilder buf; + + uint8_t data[4]; + rand->nextData(data, sizeof(data)); + + const Count charsCount = SLANG_COUNT_OF(data) * 2; + + char* dst = buf.prepareForAppend(charsCount); + + for (Index i = 0; i < SLANG_COUNT_OF(data); ++i) + { + dst[i * 2 + 0] = CharUtil::getHexChar(data[i] & 0xf); + dst[i * 2 + 1] = CharUtil::getHexChar(data[i] >> 4); + } + buf.appendInPlace(dst, charsCount); + obfusctatedPathInfo = PathInfo::makePath(buf); + } + } + + SourceFile* obfuscatedFile = sourceManager->createSourceFileWithSize(obfusctatedPathInfo, uniqueLocCount); + + // Create the view we are going to use from the obfusctated "file". + SourceView* obfuscatedView = sourceManager->createSourceView(obfuscatedFile, nullptr, SourceLoc()); + + // Okay now we want to produce a map from these locs to a new source location + { + // Create a "bag" and put all of the indices in it. + List<SourceLoc> bag; + + bag.setCount(uniqueLocCount); + + const SourceLoc baseLoc = obfuscatedView->getRange().begin; + + { + SourceLoc* dst = bag.getBuffer(); + for (Index i = 0; i < uniqueLocCount; ++i) + { + dst[i] = baseLoc + i; + } + } + + // Pull the indices randomly out of the bag to create the map + for (auto& pair : locPairs) + { + // Find an index in the bag + const Index bagIndex = rand->nextInt32InRange(0, int32_t(bag.getCount())); + // Set in the map + pair.obfuscatedLoc = bag[bagIndex]; + // Remove from the bag + bag.fastRemoveAt(bagIndex); + } + } + + // We can now just set all the new locs in the instructions + { + const LocPair* curPair = locPairs.getBuffer(); + LocPair pair = *curPair; + + for (const auto& instWithLoc : instWithLocs) + { + auto inst = instWithLoc.inst; + + if (instWithLoc.loc != pair.originalLoc) + { + SLANG_ASSERT(curPair < locPairs.end()); + curPair++; + pair = *curPair; + } + SLANG_ASSERT(pair.originalLoc == instWithLoc.loc); + + // Set the loc + inst->sourceLoc = pair.obfuscatedLoc; + } + } + + // We can now create a map. The locs are in order in entries, so that should make lookup easier. + // This doesn't "leak" anything as the obfuscated loc map is not distributed. + + RefPtr<SourceMap> sourceMap = new SourceMap; + sourceMap->m_file = obfusctatedPathInfo.getName(); + + // Make sure we have line 0. + // We only end up with one line in the obfuscated map. + sourceMap->advanceToLine(0); + + { + // Current view, with cached "View" based sourceFileIndex + SourceView* curView = nullptr; + Index curViewSourceFileIndex = -1; + + // Current handle, and store cached index in curPathSourceFileIndex + StringSlicePool::Handle curPathHandle = StringSlicePool::Handle(0); + Index curPathSourceFileIndex = -1; + + for (Index i = 0; i < uniqueLocCount; ++i) + { + const auto& pair = locPairs[i]; + + + // First find the view + if (curView == nullptr || + !curView->getRange().contains(pair.originalLoc)) + { + curView = sourceManager->findSourceViewRecursively(pair.originalLoc); + SLANG_ASSERT(curView); + + // Reset the current view path index, to being unset + curViewSourceFileIndex = -1; + + // We have to reset, because the path index is for the source manager + // that holds the view. If the view changes we need to re determine the + // path string, and index. + curPathSourceFileIndex = -1; + } + + // Now get the location + const auto handleLoc = curView->getHandleLoc(pair.originalLoc); + + Index sourceFileIndex = -1; + + if (handleLoc.pathHandle == StringSlicePool::Handle(0)) + { + if (curViewSourceFileIndex < 0) + { + const auto pathInfo = curView->getViewPathInfo(); + curViewSourceFileIndex = sourceMap->getSourceFileIndex(pathInfo.getName().getUnownedSlice()); + } + sourceFileIndex = curViewSourceFileIndex; + } + else + { + if (curPathSourceFileIndex < 0 || + handleLoc.pathHandle != curPathHandle) + { + auto viewSourceManager = curView->getSourceManager(); + const auto filePathSlice = viewSourceManager->getStringSlicePool().getSlice(curPathHandle); + + // Set the handle + curPathHandle = handleLoc.pathHandle; + + // Get the source file index. + curPathSourceFileIndex = sourceMap->getSourceFileIndex(filePathSlice); + } + + sourceFileIndex = curPathSourceFileIndex; + } + + // Create the entry + SourceMap::Entry entry; + entry.init(); + + entry.sourceFileIndex = sourceFileIndex; + + // i is the generated column + entry.generatedColumn = i; + + entry.sourceColumn = handleLoc.column - 1; + entry.sourceLine = handleLoc.line - 1; + + // Add it to the source map + sourceMap->addEntry(entry); + } + } + + module->setObfuscatedSourceMap(sourceMap); + + return SLANG_OK; +} + +} // namespace Slang |
