summaryrefslogtreecommitdiff
path: root/source/slang/slang-ir-obfuscate-loc.cpp
blob: 3ff2f3713ac0cc1f6ca7e8c8181fdedb03bdfe52 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// 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;

    // We want the hash to be stable. One problem is the source locs depend on their order of inclusion.
    // To work around this we are going 
    {
        SourceView* sourceView = nullptr;

        SourceLoc curLoc;
        for (const auto& instWithLoc : instWithLocs)
        {
            if (instWithLoc.loc != curLoc)
            {
                LocPair locPair;
                locPair.originalLoc = instWithLoc.loc;
                locPairs.add(locPair);

                // This is the current loc
                curLoc = instWithLoc.loc;
            
                // If the loc isn't in the view, lookup the view it is in
                if (sourceView == nullptr || 
                    !sourceView->getRange().contains(curLoc))
                {
                    sourceView = sourceManager->findSourceViewRecursively(curLoc);
                    SLANG_ASSERT(sourceView);

                    // Combine the name
                    hash = combineHash(hash, getHashCode(sourceView->getViewPathInfo().getName().getUnownedSlice()));
                }
                SLANG_ASSERT(sourceView);

                // We combine the *offset* which is stable
                hash = combineHash(hash, getHashCode(sourceView->getRange().getOffset(curLoc)));
            }
        }
    }

    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);

    // We have only one line for all locs, just set up that way...
    {
        const uint32_t offsets[2] = { 0, uint32_t(uniqueLocCount) };
        obfuscatedFile->setLineBreakOffsets(offsets, SLANG_COUNT_OF(offsets));
    }
    
    // Create the view we are going to use from the obfusctated "file".
    SourceView* obfuscatedView = sourceManager->createSourceView(obfuscatedFile, nullptr, SourceLoc());

    const auto obfuscatedRange = obfuscatedView->getRange();

    // 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);

        {
            SourceLoc* dst = bag.getBuffer();
            for (Index i = 0; i < uniqueLocCount; ++i)
            {
                dst[i] = obfuscatedRange.begin + 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;
  
            // Calculate the column offset, from the pair obfuscated loc
            entry.generatedColumn = Index(obfuscatedRange.getOffset(pair.obfuscatedLoc));

            // We need to subtract 1, because handleLoc locations are 1 indexed, but SourceMap
            // entry is 0 indexed.
            entry.sourceColumn = handleLoc.column - 1;
            entry.sourceLine = handleLoc.line - 1;

            // Add it to the source map
            sourceMap->addEntry(entry);
        }
    }

    // Associate the sourceMap with the obfuscated file
    obfuscatedFile->setObfuscatedSourceMap(sourceMap);

    // Set the obfuscated map onto the module
    module->setObfuscatedSourceMap(sourceMap);

    return SLANG_OK;
}

} // namespace Slang