├── .gitignore
├── LICENSE
├── docs
├── fuzztarget.html
└── nimdoc.out.css
├── examples
├── base64
│ ├── decode.nim
│ └── roundtrip.nim
├── compress
│ ├── compress.nim
│ └── compress.nims
├── fpsum.nim
└── imgread.nim
├── experiments
├── altapi.nim
├── api_exp.nim
├── memstreams.nim
├── nfpsum.nim
├── nfpsum.nims
└── tstructure.nim
├── libfuzzer.nimble
├── libfuzzer
├── fuzztarget.nim
├── fuzztarget.nims
└── standalone.nim
├── readme.rst
└── tests
├── shadowmem.nim
├── tasan.nim
├── tcmdline.nim
├── tcov.nim
├── tcrossover.nim
├── tcycleleak.nim
├── tdefect.nim
├── tfuzz.nim
├── tmem.nim
├── traise.nim
├── tstring.nim
└── ttsan.nim
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore all root files
2 | /*
3 | # Exclude source code files in subdirectories
4 | !/*.nim
5 | !/*.nims
6 | !/*.rst
7 | # Exclude root directories
8 | !/*/
9 | # Include all files in subdirectories
10 | /*/*
11 | # Exclude source code files in subdirectories
12 | !/*/*.nim
13 | !/*/*.nims
14 | # Exclude doc files in subdirectory
15 | !/docs/*.html
16 | !/docs/*.css
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Antonis Geralis
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/docs/fuzztarget.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | libfuzzer/fuzztarget
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
libfuzzer/fuzztarget
24 |
25 |
26 |
27 | Theme:
28 |
29 | 🌗 Match OS
30 | 🌑 Dark
31 | 🌕 Light
32 |
33 |
34 |
39 |
40 | Search:
41 |
42 |
43 | Group by:
44 |
45 | Section
46 | Type
47 |
48 |
49 |
50 |
51 |
52 | Procs
53 |
54 |
62 |
66 |
70 |
74 |
78 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
Source
92 |
Edit
93 |
94 |
95 |
96 |
NOTE: the libFuzzer interface is thin and in the majority of cases all you need is to define the procedure testOneInput in your file.
97 |
98 |
99 |
100 |
101 |
102 |
proc customCrossOver ( data1 : ptr UncheckedArray [ byte ] ; len1 : int ;
103 | data2 : ptr UncheckedArray [ byte ] ; len2 : int ;
104 | res : ptr UncheckedArray [ byte ] ; maxResLen : int ; seed : int64 ) : int {.
105 | exportc : "LLVMFuzzerCustomCrossOver" , ... raises : [ ] , tags : [ ] , forbids : [ ] .}
106 |
107 |
108 | Optional user-provided custom cross-over procedure. Combines pieces of data1 & data2 together into res . Returns the new length, which is not greater than maxResLen . Should produce the same mutation given the same seed .
109 | Source
110 | Edit
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
proc customMutator ( data : ptr UncheckedArray [ byte ] ; len , maxLen : int ; seed : int64 ) : int {.
119 | exportc : "LLVMFuzzerCustomMutator" , ... raises : [ ] , tags : [ ] , forbids : [ ] .}
120 |
121 |
122 | Optional user-provided custom mutator. Mutates raw data in data [ 0. .< len ] inplace. Returns the new length, which is not greater than maxLen . Given the same seed produces the same mutation.
123 | Source
124 | Edit
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
proc initialize ( ) : cint {.exportc : "LLVMFuzzerInitialize" , ... raises : [ ] , tags : [ ] ,
133 | forbids : [ ] .}
134 |
135 |
136 | Initialize Nim's internals, which is done calling a NimMain function.
137 | Source
138 | Edit
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
proc mutate ( data : ptr UncheckedArray [ byte ] ; len , maxLen : int ) : int {.
147 | importc : "LLVMFuzzerMutate" , ... raises : [ ] , tags : [ ] , forbids : [ ] .}
148 |
149 |
150 | Experimental, may go away in future. libFuzzer-provided procedure to be used inside customMutator . Mutates raw data in data [ 0. .< len ] inplace. Returns the new length, which is not greater than maxLen .
151 | Source
152 | Edit
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
proc standaloneFuzzTarget ( ) {.... raises : [ IOError , ValueError ] ,
161 | tags : [ WriteIOEffect , ReadIOEffect ] , forbids : [ ] .}
162 |
163 |
164 | Standalone main procedure for fuzz targets.
165 | Use - d : fuzzSa to call standaloneFuzzTarget to provide reproducers for bugs when linking against libFuzzer is undesirable.
166 |
167 | Source
168 | Edit
169 |
170 |
171 |
172 |
173 |
174 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 | Made with Nim. Generated: 2023-02-11 19:13:10 UTC
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
--------------------------------------------------------------------------------
/docs/nimdoc.out.css:
--------------------------------------------------------------------------------
1 | /*
2 | Stylesheet for use with Docutils/rst2html.
3 |
4 | See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
5 | customize this style sheet.
6 |
7 | Modified from Chad Skeeters' rst2html-style
8 | https://bitbucket.org/cskeeters/rst2html-style/
9 |
10 | Modified by Boyd Greenfield and narimiran
11 | */
12 |
13 | :root {
14 | --primary-background: #fff;
15 | --secondary-background: ghostwhite;
16 | --third-background: #e8e8e8;
17 | --info-background: #50c050;
18 | --warning-background: #c0a000;
19 | --error-background: #e04040;
20 | --border: #dde;
21 | --text: #222;
22 | --anchor: #07b;
23 | --anchor-focus: #607c9f;
24 | --input-focus: #1fa0eb;
25 | --strong: #3c3c3c;
26 | --hint: #9A9A9A;
27 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAAA9CAYAAADCt9ebAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjAzOjQ4KzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMjoyODo0MSswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozMzM0ZjAxYS0yMDExLWE1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");
28 |
29 | --keyword: #5e8f60;
30 | --identifier: #222;
31 | --comment: #484a86;
32 | --operator: #155da4;
33 | --punctuation: black;
34 | --other: black;
35 | --escapeSequence: #c4891b;
36 | --number: #252dbe;
37 | --literal: #a4255b;
38 | --program: #6060c0;
39 | --option: #508000;
40 | --raw-data: #a4255b;
41 |
42 | --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");
43 | --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");
44 | --clipboard-image: var(--clipboard-image-normal)
45 | }
46 |
47 | [data-theme="dark"] {
48 | --primary-background: #171921;
49 | --secondary-background: #1e202a;
50 | --third-background: #2b2e3b;
51 | --info-background: #008000;
52 | --warning-background: #807000;
53 | --error-background: #c03000;
54 | --border: #0e1014;
55 | --text: #fff;
56 | --anchor: #8be9fd;
57 | --anchor-focus: #8be9fd;
58 | --input-focus: #8be9fd;
59 | --strong: #bd93f9;
60 | --hint: #7A7C85;
61 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQy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");
62 |
63 | --keyword: #ff79c6;
64 | --identifier: #f8f8f2;
65 | --comment: #6272a4;
66 | --operator: #ff79c6;
67 | --punctuation: #f8f8f2;
68 | --other: #f8f8f2;
69 | --escapeSequence: #bd93f9;
70 | --number: #bd93f9;
71 | --literal: #f1fa8c;
72 | --program: #9090c0;
73 | --option: #90b010;
74 | --raw-data: #8be9fd;
75 |
76 | --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");
77 | --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");
78 | --clipboard-image: var(--clipboard-image-normal);
79 | }
80 |
81 | @media (prefers-color-scheme: dark) {
82 | [data-theme="auto"] {
83 | --primary-background: #171921;
84 | --secondary-background: #1e202a;
85 | --third-background: #2b2e3b;
86 | --info-background: #008000;
87 | --warning-background: #807000;
88 | --error-background: #c03000;
89 | --border: #0e1014;
90 | --text: #fff;
91 | --anchor: #8be9fd;
92 | --anchor-focus: #8be9fd;
93 | --input-focus: #8be9fd;
94 | --strong: #bd93f9;
95 | --hint: #7A7C85;
96 | --nim-sprite-base64: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAARMAAABMCAYAAABOBlMuAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFFmlUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxNDggNzkuMTY0MDM2LCAyMDE5LzA4LzEzLTAxOjA2OjU3ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgeG1sbnM6cGhvdG9zaG9wPSJodHRwOi8vbnMuYWRvYmUuY29tL3Bob3Rvc2hvcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RFdnQ9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZUV2ZW50IyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgMjEuMCAoV2luZG93cykiIHhtcDpDcmVhdGVEYXRlPSIyMDE5LTEyLTAzVDAxOjE4OjIyKzAxOjAwIiB4bXA6TW9kaWZ5RGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgeG1wOk1ldGFkYXRhRGF0ZT0iMjAxOS0xMi0wM1QwMToyMDoxMCswMTowMCIgZGM6Zm9ybWF0PSJpbWFnZS9wbmciIHBob3Rvc2hvcDpDb2xvck1vZGU9IjMiIHBob3Rvc2hvcDpJQ0NQcm9maWxlPSJzUkdCIElFQzYxOTY2LTIuMSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIiB4bXBNTTpPcmlnaW5hbERvY3VtZW50SUQ9InhtcC5kaWQ6ZWRlYjM1NzAtYjZmYy1kMjQ0LWExMWYtMjI3OWJmODQzYWEwIj4gPHhtcE1NOkhpc3Rvcnk+IDxyZGY6U2VxPiA8cmRmOmxpIHN0RXZ0OmFjdGlvbj0iY3JlYXRlZCIgc3RFdnQ6aW5zdGFuY2VJRD0ieG1wLmlpZDplZGViMzU3MC1iNmZjLWQyNDQtYTExZi0yMjc5YmY4NDNhYTAiIHN0RXZ0OndoZW49IjIwMTktMTItMDNUMDE6MTg6MjIrMDE6MDAiIHN0RXZ0OnNvZnR3YXJlQWdlbnQ9IkFkb2JlIFBob3Rvc2hvcCAyMS4wIChXaW5kb3dzKSIvPiA8L3JkZjpTZXE+IDwveG1wTU06SGlzdG9yeT4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4JZNR8AAAfG0lEQVR4nO2deViTZ7r/7yxkJaxJ2MK+GCBAMCwS1kgUFQSKK4XWWqsz1jpjp3b0tDP1V+eqU391fqfT/mpPPd20drTFDS0KFEVWJSGAEgLIZpAICBJACIRs549Rj1WILAkBfD/XlevySp68z/0S3+/7vPdzLyidTgcLkU2bd+z39/f/q1gshsrKSoJELFCa2iaEuU9K6kb+8uXxv54/fzE8L/eswNT2zCfQpjbAGKS8lPFKSEjIXiaTCSEhIeDj4xNnapsQ5j6rktZGp6UlfxIdzQVzCplmanvmG1hTG2BIAtlc26CgoDfT0tL2e3l5AQCAjY0NkMnk/a9s2k6rrKw8UV8n1JjYTIQ5RlAw14KzmL3xze1vfJyUuMJaq9UCFovFm9qu+YbBxcSPFUYkk8l2Q0NDsvo6ocrQx5+I8Ih4bz6f/0l8fHyKlZXV4/dRKBQwmcwwMpn8A4FAoPgHhH9bV1sxa488wZxoaycnJ/a9e/duCa5fkc3WvAiTI4Ib77p+XdqHG9anbfLy8gAAgLGxMdBpF+bjvzExqJj4scKI0dHRnwQHB++orq7+AgDeMuTxJ2Jl4rqU9PT0EwEBAUQCgTDuGAaDAampqYepVKpHUHDk325Ulw0a266YuFW+Gzdu/MDPz29jfn7+XgA4aOw5ESZP6kvpCXv3vnM8NiaSamVl+fj9BepGNDoGFRN7e/slcXFxO1xcXMDJyWnH7j//H/fi4uJdgutXmgw5z5O8smn7X9euXbvf29sbMBjMhONQKBRYWVlBbGzsbjMzM3JoOG+/sKKwy1h2rd/4elpGRsYuLy+vaDweD2w2Oy1h5ZrCvEunEaeeiVnMiabyl/F2/+X9P+8JDPQHHA5napMWBAYTk6DgSNuEhIS9DAYDAP7tq1i6dOkqOp3OWbNu0wens44emeoxA9lcWwKBYEMkEm2JRKIdHo+3QKFQWJ1Op8ZgMER3d/dVq1evTnFycpr0MSkUCsTExGzH4/Gk1LTME/39/TI0Go1FoVCg1WrVY2NjipGRkcGRkRH5dPwrEZHLXMPCwjJSUlIy3dzcfB+97+rqGhYSEpIOAIiYmBguN3zL77dt3uPh4W5qUxYUBhMTb2/vjeHh4cvR6P/dILK0tITIyEg7BweHr363/Z3Ampqaf1Zcu/zMKiVsyVJvMplsRyKR7IhEor2FhYUbhUJhJCYm2pFIJB6JRAIymQx4PB7QaDRoNBowMzMDJycnwOOn7icjEokQGxu7icFgbLp///7jFY1WqwWlUgkjIyOgUCgO7Ni5Rz48PCwfHh7uGRkZeaBQKOSjo6ODCoVCXlNVKn/6uCsT13FXrVr1emho6BYKhfLMnP7+/omrU9LPX8g+UThloxEMxqJFXjxESAyPQcSEExrLWLNmzW57e/txP/fw8ABHR8cdDAaDt3xF2ru9vb03sVgs0cbGxs/FxWVZUlISj0aj+dna2oKtrS1M5PcwJCgUCry8vODRrs84vPfoH6OjoyCXy6Gvr+/R6+CWrX9s7evrk/b19bWr1Wqli4sLZ8OGDe95eXmxUSjUuAd0cHDwjoqK2sYKXFIhvnldYYTTQpgU4/8+jyASCYDGoCd+ZkYYF8OICYezl8PhuOkbQyAQIDo62s/NzS2np6cHbGxsgEajAYFAAAwGA1gsFia6CE0NgUAABwcHsLe3B61WC2q1eo9WqwWNRgNKpRLUajUQiUSgUCh6zwGHwwGTydzo5+eXBQBnZu8MEJ5keHhYPqyYWMtHR0ZBpVIhYj9FUDONgOUvT12+du3avMDAQJjssdRqNWCxCyrEZdLodDoQi8Ulx44de628NL/V1Pa8iERE8l2dHB2CJvpcq9Nqbt1qKURWj1Njxld0ZGTkAW9v70kLCQC8sEIC8O/HKx8fn2gmk8kHgCk7pRFmzrWyAikASE1tx0Jj2uH0EZHL/N7YtuvT4OBgzmz4OBYSeDweIiMjt2S++vtMP1YYEmmJsCCY8mNOIJtr6+zsHBcZGXmIw+G4mZubG8m0hU9HRwcUFxe/KxQKTyDRsQjznSmJCS9+dVRERMTfQ0NDo2xtbfUGiSFMjtHRUaitrc3Jzc09kHvxVLmp7UFAmC6oZQkvrZLL5RJhReHtiQb5scKIXC7371FRUX90dnYGIpE4JR8Jgn40Gg20t7fXFxYWfnr9+vWjz8sdYi+Osh4vzgUBwZSgtu94V+fs7Hx7YGCgra6u7khLS0u2RCwYeTQgKmYFh8fj/f/g4OAldnZ2prR1wdPd3Q1CofBQSUnJkdLi3N8E93FCY6k+Pj48FxcXjlar1ZSWlh65VvYr4kREmDNg79+/D3FxcW5OTk5uXl5evNbW1tL0jK3ZXV1d1ykUintycvInoaGhdkj+gvGxs7MDPp+/m0AgWMQvS/lyeHhYTqPRPJycnIJSU1NZ3t7eW2g0Gly/fv2oWq1Gij0hzClQ/gHhpLS0tEM8Hm/7I8Ho7++HlpYWsLa2Bg8PDxOb+OKhUCigqakJ7t+/D25ubuDu7g4oFAp0Oh08ePAAvv7666TTWUdzTG0nAsKTYMU3ryuSU18+4+bmFrZo0SIOAICVlRUsXrx4zkakLnRIJBI8CgJ8MtdJp9NBZ2enqL29XWRC8xAQxgUNAHD+3L8KGhoaCp78ABES04JCoX4jJAAAAwMDUFtbe96YpRMQEKbL41DU5ubmko6Ojj2PSgggzD36+/vrb9y4cX425zzw93/8EBjon2is44+NjSkePBjqGRwc7G5v7xBV19w8U5B/3qgrr9+/uWtXUuKKD/TZ9MXh/066/OuFmunO8dGBQ98HBbGSp/t9U6LRaDXK0dHBoeFhuVzeL22/0yFqamopufjLqRJ933ssJi0tLSXV1dWHGAzGbuObOzs8ubqa71vZKpUKOjo6blwpOF8zm/Mu5cVkLlkSaswprAHAaVihgK7O7oSGxltvfXLon3nXK4RHT2cdN4pfKDCAlZyUuMJan02nTmczAaBmunPw4qI3cbnh0/36XICq0+lgcPABp7OrK629vUP5z8++LLh2XXD05L++yxrvC4/F5EZ12WBS8saLS5Ys2U2lUufUY45SqQSlUgkqlQrUavXj19jYGGg0GtBoNKDT6UCn05VotVq1TqfToFAojFar1eh0Og0Wi8XhcDgeGo1+/PhgZmYGOBwOsFgsmJmZ/eY1F+nt7YXa2trs2Z73wdCQBgCMHp1IJpHA09MdPD3dLRIS+OtKisvWvbP7vf2lZdePVFwzbHTwyMiI3hidkZFRUKvUYzOZ48HQkBIA5nWqBAqFAktLC7C0tADmIh88Pz4uMSyUk7hn776DV4tKPn/6d/lNxp1MJqsRCASf8vn8XdMpOjRTVCoVjI2NgUqlAq1WCyMjI9DX1wf379+Hvr6+/Q8ePOgdGRmRKxSKx0WLFAqFXKlUKnQ6nUar1arHq47mxwrD4/F4Eg6HI2GxWDwej7cgkUjWFAqFam5uTjU3N6eRyeQPLSwswNraGqysrIBAIDwWFywW+zja11Qi29LSclIikeSZZPJZBovBAI8XA8HBQR9kZZ3lR8cmvFZSlGe00p8IkwONRkNERBj4+i7a4+XpHv307/IbMakWlciXJbx0nMPh7Jqo0JGh0el0MDo6Cl1dXSCVSkEmk7177969W319fe1DQ0M9KpVKoVarlWq1WjndNhUPG3ApAWDcOxLTLwSDwWAOotFoDBaLxRMIBAsrKysne3t7Xzqd7k2n0/c4OzsDlUoFHA4364IyMDAATU1NxdWikhcq6tXKyhJezljPJZKI2eERS5cZeoWCMD2srCwhPX0tVzk2djiCG//GtfLLUoBxShB0dHTU3Lx580sLC4vtJBLJKMZoNBqQSqUglUqPdnR01PT09DT19/fLHjx40DM0NNQ72933GiSVGgB4JFQK+LfoSAGgnL04yppEIh2xtLS0t7GxcaFSqR7Ozs4fMRgMcHR0nJX8pJs3b54Ui8UXjT7RHIRMIkFK8irfwcEHPwQELUmqvYHUGJkLmJubw8YNa/i9vfffY/px3myQiDTPiEl9nVDDX576jaenZ7SnpyfLUJNrNBqQyWRw+/bt4x0dHTdkMlltV1dXw/XygjkdEv4wB0YOAK0AUM70C8HQ6fSzdDrdm0qlejg6OrLc3Ny2MBiMadWjfR4PHjyAmzdvZs/1v5MxoVAokJK8iicWS95k+nH+s0EiQhqpzQGoVFtYk5a87ba0XQAA34xbpagg/5zoT7s/OGNnZ8eaaYkBuVwOnZ2d5VKpVNTS0lLS2NhYWFVZ3Dujg5qQh6uY+ocvCAiKIPn4+Jz19PSMdnV15VCpVL6Dg4NBViw6nQ5EItHRpqamqzM+2DzHzo4O69amftLQeKsAZrDLgmBY/PyYsCIhfs+SiKUFE5Y8EwqFx11cXDihoaFTjjFAoVAwPDwMHR0dourq6jNCofDHhZqUVnvjmgIAcgAgJyg40mLRokX8kJCQjT4+PussLS1n1JPl7t27UFxcfHguB6mNjY2B7G4naNRTWyygUCjAYDGAx+PB0sICSCSi3vFYLBbCwjjA8vddBQtATKb7d3saBwc7IJPJBpsHjUGDGRYLJBIJLK0sAfucmyIGg4FFi3y8AwNZtycUk5KiS02vvf7WWQaDkejg4DApQwAeh3xDaWnpPoFAcPxFqnP6sEvgGf+A8Bx3d/cvIyIiNi1evHjT8wpNj8fAwACUlZW9P9dD5+/ckcFbf9gd2dcnn9LNAovF4inmZHtXNxdOdBR3+/JlS33pdP29wolEInA4weuiYxOy5vvuTkeHDHb+8c8xvb33Z3R9/N+Df+uIjYk02DwkEsna2trS1d/fNyGeF7uTyw1/7g3R3t4O2OxA/TVghULhcQqFQk1JSfmYSNR/5wD4d6EfgUBwvLS09IhUKhW9qAV5H9YjKQwJi6uvrKw8ERoamhkSEpKp7w7yJEqlEiQSyZmysrJv53qjdaVSCZdyTk+3qFMrAJRHRPLPN95qeifj5fU7mYt8JhyMRqMhMJDFdnF25gDAvBYTpXIMWlpay2fq/8m5mDcIABYGnEcGAGI/VlhBZWX1yZdSkz55OX0dV5+7w9bGGvz8mPrFpK62QskJjf2GTqd7x8bGbpnID4BCoUAmk0lLSkqOiESik2UleS/MakQflYKrXQDQxY1a3tTe3i6KiIjY5OXlxX7e9+rr6wsuXbr0t4ffn9OgMWjghMZQRcLp+8GulRVI/QPC37Wxtnal0ajJtjY2E451ZjiBra31vE9lR2PQQKFQaAAwo98Yi8Xq9fpPd56HO6rlvKWJv/PwcK+JilyCmajWMw6HAzs7+rMFpQOCIn6zHywSFvXm5eUdFAqFZ9Rq9bgHa2trq79w4cK+zz49cAARkmcpL81v/a/Dhz49d+7c3qqqqjyVSjXuOJ1OBxKJpDw3N/fA5V+zax6978cKw/sHhM/raMrnUVdboSy4fPWQSFSjd5yFBQWIRNKEd2IEw1J4JUd88WL+R51d3XrHWVDMnxUTa2tr1zXrNiUGsrmPf7DS4tymCxcu7Kuurs55+kKQSqVN586d23vs+8NHDXUCC5Wzp3/Iy8rKeruysvLM2Nhvo7VVKhXU1tYWnj17du/T7UOdnZ2D7OzsfGGB09raVi4S1RzXl0eFw+EAj8chYjKLVFffyOrq1C8mJBLpWTFRKBRyDofzC4vFWvXk+1ev/CLOzs7eKxAIslQqFeh0Oujp6enKzs7em/XTd7OayTqfKb56sT4rK+sPAoHg5KO/o0KhAKFQmHXy5MkdF3/5+TeZmctXpIXZ29v7zqVcKWNRX1epuXu3U/y8pEw0GmndOZt0dnXVDw0P6/W5oNHoZ30mQ0NDPb29vfvj4+Pf3rR5B/7od188XnEUXr4gDgmL+0NfX5/U19d3d3l5+YGfTnyDtLmcIhXXLsu4UcvfR6PRGGtra9eysrIjYrE45+kt4Fheou/69es/unnz5vm7d+/Wmsre2WRkZGTQ1DYg/JYGiUiTm1ugBAC9IfHPiEmDpFITE7fqJI/H27lmzZpDq5LWtz55t6wUXO3ihMYerK+vz2tpaUFaM0yT8tL81ujYle+TSCTrvEunBU9/voTLd92wYcPHVCqV39XVdXCu7+oYCp1O90Kc50Jk3I5+xVcv1jc3N5d4enpSMzIyvkpK3sh78nORsKg3++yPBS/q1q+hKCm61DSekERGJ3ikp6d/ERsbm1xVVXWwtbX1hRFtFAqFPMLMUyZsDyoQCI7LZDKIiIjwzczM/GpV0vro2TTsRSUqZoX3+vXrP1u9enXi0NAQiESirIdRtggIc5oJ40zq6uryGhoa8ry8vBJCQ0O9USjU94mrN7yWc+EnvaXb5gJMvxCMp6cnl0Kh2Le1tZVXXLs8L1LXefGrWRkZGZ/x+XyeUqkEkUh0vqenZ14HZyG8OEwoJjdrygd37NxTEBkZmWBtbQ3BwcEeKBTq+/UbX3/355Pfzlmn66qk9dGbN29+k8PhbCSRSNDZ2Snb9ae/HCkpKTksEhbN2QTD5NSX+Vu3bj0cHBzsjcFg4O7du1BWVvbNwxB9BIQ5j94I2Fu3bhXW19cDl8sFLBYLHA7Hg0wmf/e77e84ffXlPz6fLSMnQ2paZkJ4eHjmtm3b+B4eHvZkMhlQKBTY29s72dvbfxgUFJT8x7ffP1NRUfHjXErnZ/qFYKKjo7dt3rz5g8DAQPtH/XHa2tpqGhsbC55/BASEuYFeMblz505NTU3NgfDw8PcwGAygUCjw9fW1IJPJn/1130Hv0tLSI4WXL4hny9inYS+Osvbz80tgMpn8jIwMPovFch2vpoiDgwM4ODhwfH19OYsWLeJv3/Hu+cbGxquzXZz5aZYlvMRJT0/fFhkZue3JZmfd3d0gEolOIr4ShPmEXjFpkFRqXlrzSnFnZ+d7Tk5OjzNfXVxcICMjY6ezszNnVdL6vU8HWhmbgKAIkrOzMyc1NTXz0YU4maAuOp0OK1as4EVFRfGEQqHg1dfePHzr1q2rs71S8WOF4f38/BLS09M/iIyM5DxdxLq5uVlcVVU1bgVwBIS5il4xAQCQyWRigUBwJikpKe3JVGQcDgdLly7l2tranti0ecf7IpEoy9hbxX6sMDydTvdevXr1ltjY2F3u7u6AxT73FJ7B3Nwc4uLiwthsdphQKCzZkL7l0/r6+oKbNeVG90+EhMXZL1++fFtycvKHrq6uz4igUqmE5ubmEiTHCWG+8dwrUXD9imz9xtd/jIuLS7N5KpsTjUZDUFCQE4PB+F4oFGYmJW888Mv5k4UTHGpGxC9LYaenp78VEhKyxdHRESgUyoyOh0KhwNraGuLi4qIDAgKi6+rqyjekb/mHMSN6N6RvSdu+ffseNpsdZm09ftuW+vp6EIvFSB9hhHnHpG7rUqm0orW1tdXS0tLj6TIEaDQaaDQaxMfH811dXTl/3Xfw+JUrVz411J01cfWG6IiIiC07d+5McHNzs7ewMGyOFw6HAwcHB6BSqVx3d/fwz7/4rkAgEBwXCoUnHpZonDGrU9J5MTEx27du3Zrm4uKC0beaqq6u/ry+vj7XEPMiIMwmkxKTimuXZe/u+fCkp6fnexPdUfF4PPj7+1szGIydLi4unF1/+kvenTt3RG1tbRXTqfma8lIG39/fP/HVV19NZrFYHpMpzjQTzMzMwNPTE+Pp6Zng6emZ4Ofnl5CesfV8bW1tznQe3/wDwvFeXl7Rvr6+Ca+88kpaUFCQh74GXzqdDrq7u6GpqankRQmdR1hYTNrhUFVVlcXj8d6ysrKy0OfstLS0hPj4eC6Xy+U2NzeDRCI5/sa2XeX37t1rGhwc7BoYGJBN1P+FFbiE5OzszGaxWImvvvrqpoCAAKfp+ERmCpPJBCaTmcnhcDJLS0u/TE59+YxUKhXoi/lg+oVgrKysGJaWlna2trYeaWlpXDabvTMgIGDSfp2KiorzbW1tL0zoPMLCYtJX6uVfs2u++PKowMPDgz+ZIslEIhECAgKAxWJlajSazJ6eHmhra4PW1tZvtmz9o6Czs7O+r6+vfWxsbFir1WosLCzsV6xYkcnj8d7z9vaelmPV0Hh5eYGnp+f2mJiY7UVFRZ/HL0v5tru7+5ZGo1FisVg8Docj4fF4CxsbG1c+nx/m7e39sYeHB7i4uIC5ufmU6r4ODQ1BZWXlifkSrYuA8DRTumIrKytPent78728vCb9HRQKBVgsFhwcHIBOpwObzd4yNja2RaVSwdDQEHR1dcHo6CjQaDRwdXWdsWPV0KBQKPDw8AA7O7udERERO2tra2FgYACoVCo4OTkBjUYDMpkMeDz+8WuqaLVaaGxsbL19+/YzSX8ICPOFqYrJidDQ0AwvLy/e80c/CwaDARKJBI86BdJoNHB3dwe1Wj0nViL6IJPJwGQywdnZGZRKJRAIBDBUx8OBgQEoLS39BtkORpjPTJg1PB61N64pmpqarvb39xvUiLkuJE9CJpPBxsbGYEICANDZ2SlHgtQQ5jtTEhMAgLq6ulyJRFJvDGNeREZGRkAikRSUFuci2cEI85opi0l+7hmBWCzOeV6dToTJcfv27cHr168jxbgR5j1TFhMAgObm5hKZDNl0MAQtLS3Xzpw6hkS8Isx7piUmUqlUIBAIJuyjgzA5Ojs7QSKRINGuCAuCaYmJsKKw68qVK59KJJIu5HFneiiVSigqKjouEolOmtoWBARDMC0xAQC4+MvPJadOnXq3ra1N8yL0dDEkOp0OSktLy/Pz8w8+3d4CAWG+Mm0xAQA4fuy/jl+8ePGju3fvGsqeBY9Wq4XKysrWU6dOvX31yi8mKyyFgGBoZiQmAAD/79D+fadPn96PCMrz0el0UFVV1frtt9+mj9fiAgFhPjNjMQEAyMvLO3Ds2LE/tLS0INmuerh27Vr9999//xoiJAgLEYOEntbVVigB4PNNm3cMpqSkfMRms50McdyFgkqlgqKiovJTp069nZ97BhEShAWJQePYj373xdF1GzbLFQrFx6Ghob766ne8KNy7dw+KiopO5ubmfmTK4tsICMbG4EkxWT99d35l4rre/v7+D0NCQvh0Ot3QU8wL1Go1SKVSTX5+/sH8/PyDSP8bhIWOUTLsLuVklQcFR65pbGzcvnLlyvfc3NwsCASCMaaac+h0OhgaGoLq6uqaCxcu/OV01tGcTw7uM7VZCAhGx2jpug/vxAd58atzoqKitq1cuXKnvb29saabE+h0Oqiurpbm5eUdrK6uPlspuDrvY0hmO4YIhUIBGq1/X2CmNqFQKL3/79HomZ/z82xEowyy9zFr80zGDqPn/hdeviBmL47ad+fOnRsRERGbQkNDo62srIw97azT2dkJxcXFx0tKSo7Mdh8hY4LD4TDPH2U4MFjMc6tLmZmZzaj+Aw6H0/t9PB4PGCxmRudNJBL0ngeZTAI0Gj3jv+1szfM88Hic8cUEAKCmqlQOAN/ELU2qkEgkySwWK3HRokVcBoMxG9MbDZ1OB83NzdDU1FRQW1t7XiAQHJ+ovu18pbr6Rg6L5ZtoM0EhcUPT0tJW8tWRb0vQqIkvgKqqmhnVfrl2TfANXo+gjKlUio4OWc1M5sjOzjnQUH8rbqLPu3t6moaGhmfc+3q25tGHUqmECoEIUKbIrVkcEkONiIh4jcvlvu7s7OxLo9GmVe7QVCgUCujq6oKGhoaCioqKo9XV1WeM3YDMVPDik1gpyas+XrVyeaKXl8czjyANjbcgI/MNmkg49Q4ECPOH3NyC4RUr+M8IcHt7B1y9WlKRl3/5kElKnD1sfXEoJCzueEBAQGJYWFgGk8nk2djYAIFAgLm4pTw6Ogqjo6Mgl8vhxo0b50tLS4/U19fnLvS2FIWXfxEDQNLmLW9ueW1TxtchHDaQyWRTm4VgYkZHR6G+vhF+/NfP+y5e+vVjiVgwZpKVydOwF0dZW1lZOTGZTD6bzU4LCAiIptPp8HTDL1MwOjoKLS0tUFdXd1IsFudIpdKKgYGB7tloJTrX4MUnsVJTEj9etzY10dHRAQAAGm81wcsZW5CVyQInL69gNCGBjwcAGBx8ANnncypOnTr3H9nn/reD55wovvrQpyIHAHFUzIocGo3mQaPRfBwdHVlubm7bXF1dgcFgABqNNvruglwuh7t374JMJoOOjo7P79y5I+ru7m7q7e1tXQi7MzOh8PIv4pCw2DdaWtte37Au7aPIyCWAxWABjUbPif9HCMbjURtKiaQBfvr5zH9evlJ0uLQ4r/nJMXNiZTIRrMAlJAcHB18HBweWo6Mjy8rKajeJRAJLS0uwtLQECwsLoFAogMfjAYvFgpmZ2XNXMyqVCoaHh2FoaAiGh4cfvwYGBqCvrw+6u7vfvnfvXlNvb29rT09Pq0QsUM7S6c4rNqS/lrZ5U+YPRBKR9M7u9xwqBUUvtNAudH766XSLE8PR49ixE78/8tVnX403Zk7fUR46NUUAIPIPCMdTKJTdNjY2QKPRgE6nA51OB1tbWyCRSIDD4YBAIAAejwcCgfDYUajVakGlUoFarQadTvfY79HX1wf9/f0gl8tBLpfDvXv3HvXw+dxQPYYXMj+d+P7Mmzv+5OHr6/OJWq1GBHeB09TcUiKuq/coKS3/eqIx/wPkiIXC3w6YjAAAAABJRU5ErkJggg==");
97 |
98 | --keyword: #ff79c6;
99 | --identifier: #f8f8f2;
100 | --comment: #6272a4;
101 | --operator: #ff79c6;
102 | --punctuation: #f8f8f2;
103 | --other: #f8f8f2;
104 | --escapeSequence: #bd93f9;
105 | --number: #bd93f9;
106 | --literal: #f1fa8c;
107 | --program: #9090c0;
108 | --option: #90b010;
109 | --raw-data: #8be9fd;
110 |
111 | --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E");
112 | --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E");
113 | --clipboard-image: var(--clipboard-image-normal);
114 | }
115 | }
116 |
117 | .theme-select-wrapper {
118 | display: flex;
119 | align-items: center;
120 | }
121 |
122 | html {
123 | font-size: 100%;
124 | -webkit-text-size-adjust: 100%;
125 | -ms-text-size-adjust: 100%; }
126 |
127 | body {
128 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
129 | font-weight: 400;
130 | font-size: 1.125em;
131 | line-height: 1.5;
132 | color: var(--text);
133 | background-color: var(--primary-background); }
134 |
135 | /* Skeleton grid */
136 | .container {
137 | position: relative;
138 | width: 100%;
139 | max-width: 1050px;
140 | margin: 0 auto;
141 | padding: 0;
142 | box-sizing: border-box; }
143 |
144 | .column, .columns {
145 | width: 100%;
146 | float: left;
147 | box-sizing: border-box;
148 | margin-left: 1%; }
149 |
150 | @media print {
151 | #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby {
152 | display:none;
153 | }
154 | .columns {
155 | width:100% !important;
156 | }
157 | }
158 |
159 | .column:first-child, .columns:first-child {
160 | margin-left: 0; }
161 |
162 | .container .row {
163 | display: flex; }
164 |
165 | .three.columns {
166 | width: 25.0%;
167 | height: 100vh;
168 | position: sticky;
169 | top: 0px;
170 | overflow-y: auto;
171 | padding: 2px;
172 | }
173 |
174 | .nine.columns {
175 | width: 75.0%;
176 | padding-left: 1.5em; }
177 |
178 | .twelve.columns {
179 | width: 100%;
180 | margin-left: 0; }
181 |
182 | @media screen and (max-width: 860px) {
183 | .three.columns {
184 | display: none;
185 | }
186 | .nine.columns {
187 | width: 98.0%;
188 | }
189 | body {
190 | font-size: 1em;
191 | line-height: 1.35;
192 | }
193 | }
194 |
195 | cite {
196 | font-style: italic !important; }
197 |
198 |
199 | /* Nim search input */
200 | div#searchInputDiv {
201 | margin-bottom: 1em;
202 | }
203 | input#searchInput {
204 | width: 80%;
205 | }
206 |
207 | /*
208 | * Some custom formatting for input forms.
209 | * This also fixes input form colors on Firefox with a dark system theme on Linux.
210 | */
211 | input {
212 | -moz-appearance: none;
213 | background-color: var(--secondary-background);
214 | color: var(--text);
215 | border: 1px solid var(--border);
216 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
217 | font-size: 0.9em;
218 | padding: 6px;
219 | }
220 |
221 | input:focus {
222 | border: 1px solid var(--input-focus);
223 | box-shadow: 0 0 3px var(--input-focus);
224 | }
225 |
226 | select {
227 | -moz-appearance: none;
228 | background-color: var(--secondary-background);
229 | color: var(--text);
230 | border: 1px solid var(--border);
231 | font-family: "Lato", "Helvetica Neue", "HelveticaNeue", Helvetica, Arial, sans-serif;
232 | font-size: 0.9em;
233 | padding: 6px;
234 | }
235 |
236 | select:focus {
237 | border: 1px solid var(--input-focus);
238 | box-shadow: 0 0 3px var(--input-focus);
239 | }
240 |
241 | /* Docgen styles */
242 |
243 | :target {
244 | border: 2px solid #B5651D;
245 | border-style: dotted;
246 | }
247 |
248 | /* Links */
249 | a {
250 | color: var(--anchor);
251 | text-decoration: none;
252 | }
253 |
254 | a span.Identifier {
255 | text-decoration: underline;
256 | text-decoration-color: #aab;
257 | }
258 |
259 | a.reference-toplevel {
260 | font-weight: bold;
261 | }
262 |
263 | a.nimdoc {
264 | word-spacing: 0.3em;
265 | }
266 |
267 | a.toc-backref {
268 | text-decoration: none;
269 | color: var(--text);
270 | }
271 |
272 | a.link-seesrc {
273 | color: #607c9f;
274 | font-size: 0.9em;
275 | font-style: italic;
276 | }
277 |
278 | a:hover, a:focus {
279 | color: var(--anchor-focus);
280 | text-decoration: underline;
281 | }
282 |
283 | a:hover span.Identifier {
284 | color: var(--anchor);
285 | }
286 |
287 |
288 | sub, sup {
289 | position: relative;
290 | font-size: 75%;
291 | line-height: 0;
292 | vertical-align: baseline; }
293 |
294 | sup {
295 | top: -0.5em; }
296 |
297 | sub {
298 | bottom: -0.25em; }
299 |
300 | img {
301 | width: auto;
302 | height: auto;
303 | max-width: 100%;
304 | vertical-align: middle;
305 | border: 0;
306 | -ms-interpolation-mode: bicubic; }
307 |
308 | @media print {
309 | * {
310 | color: black !important;
311 | text-shadow: none !important;
312 | background: transparent !important;
313 | box-shadow: none !important; }
314 |
315 | a, a:visited {
316 | text-decoration: underline; }
317 |
318 | a[href]:after {
319 | content: " (" attr(href) ")"; }
320 |
321 | abbr[title]:after {
322 | content: " (" attr(title) ")"; }
323 |
324 | .ir a:after,
325 | a[href^="javascript:"]:after,
326 | a[href^="#"]:after {
327 | content: ""; }
328 |
329 | pre, blockquote {
330 | border: 1px solid #999;
331 | page-break-inside: avoid; }
332 |
333 | thead {
334 | display: table-header-group; }
335 |
336 | tr, img {
337 | page-break-inside: avoid; }
338 |
339 | img {
340 | max-width: 100% !important; }
341 |
342 | @page {
343 | margin: 0.5cm; }
344 |
345 | h1 {
346 | page-break-before: always; }
347 |
348 | h1.title {
349 | page-break-before: avoid; }
350 |
351 | p, h2, h3 {
352 | orphans: 3;
353 | widows: 3; }
354 |
355 | h2, h3 {
356 | page-break-after: avoid; }
357 | }
358 |
359 |
360 | p {
361 | margin-top: 0.5em;
362 | margin-bottom: 0.5em; }
363 |
364 | small {
365 | font-size: 85%; }
366 |
367 | strong {
368 | font-weight: 600;
369 | font-size: 0.95em;
370 | color: var(--strong); }
371 |
372 | em {
373 | font-style: italic; }
374 |
375 | h1 {
376 | font-size: 1.8em;
377 | font-weight: 400;
378 | padding-bottom: .25em;
379 | border-bottom: 6px solid var(--third-background);
380 | margin-top: 2.5em;
381 | margin-bottom: 1em;
382 | line-height: 1.2em; }
383 |
384 | h1.title {
385 | padding-bottom: 1em;
386 | border-bottom: 0px;
387 | font-size: 2.5em;
388 | text-align: center;
389 | font-weight: 900;
390 | margin-top: 0.75em;
391 | margin-bottom: 0em; }
392 |
393 | h2 {
394 | font-size: 1.3em;
395 | margin-top: 2em; }
396 |
397 | h2.subtitle {
398 | margin-top: 0em;
399 | text-align: center; }
400 |
401 | h3 {
402 | font-size: 1.125em;
403 | font-style: italic;
404 | margin-top: 1.5em; }
405 |
406 | h4 {
407 | font-size: 1.125em;
408 | margin-top: 1em; }
409 |
410 | h5 {
411 | font-size: 1.125em;
412 | margin-top: 0.75em; }
413 |
414 | h6 {
415 | font-size: 1.1em; }
416 |
417 |
418 | ul, ol {
419 | padding: 0;
420 | margin-top: 0.5em;
421 | margin-left: 0.75em; }
422 |
423 | ul ul, ul ol, ol ol, ol ul {
424 | margin-bottom: 0;
425 | margin-left: 1.25em; }
426 |
427 | ul.simple > li {
428 | list-style-type: circle; }
429 |
430 | ul.simple-boot li {
431 | list-style-type: none;
432 | margin-left: 0em;
433 | margin-bottom: 0.5em; }
434 |
435 | ol.simple > li, ul.simple > li {
436 | margin-bottom: 0.2em;
437 | margin-left: 0.4em }
438 |
439 | ul.simple.simple-toc > li {
440 | margin-top: 1em; }
441 |
442 | ul.simple-toc {
443 | list-style: none;
444 | font-size: 0.9em;
445 | margin-left: -0.3em;
446 | margin-top: 1em; }
447 |
448 | ul.simple-toc > li {
449 | list-style-type: none; }
450 |
451 | ul.simple-toc-section {
452 | list-style-type: circle;
453 | margin-left: 0.8em;
454 | color: #6c9aae; }
455 |
456 | ul.nested-toc-section {
457 | list-style-type: circle;
458 | margin-left: -0.75em;
459 | color: var(--text); }
460 |
461 | ul.nested-toc-section > li {
462 | margin-left: 1.25em; }
463 |
464 |
465 | ol.arabic {
466 | list-style: decimal; }
467 |
468 | ol.loweralpha {
469 | list-style: lower-alpha; }
470 |
471 | ol.upperalpha {
472 | list-style: upper-alpha; }
473 |
474 | ol.lowerroman {
475 | list-style: lower-roman; }
476 |
477 | ol.upperroman {
478 | list-style: upper-roman; }
479 |
480 | ul.auto-toc {
481 | list-style-type: none; }
482 |
483 |
484 | dl {
485 | margin-bottom: 1.5em; }
486 |
487 | dt {
488 | margin-bottom: -0.5em;
489 | margin-left: 0.0em; }
490 |
491 | dd {
492 | margin-left: 2.0em;
493 | margin-bottom: 3.0em;
494 | margin-top: 0.5em; }
495 |
496 |
497 | hr {
498 | margin: 2em 0;
499 | border: 0;
500 | border-top: 1px solid #aaa; }
501 |
502 | hr.footnote {
503 | width: 25%;
504 | border-top: 0.15em solid #999;
505 | margin-bottom: 0.15em;
506 | margin-top: 0.15em;
507 | }
508 | div.footnote-group {
509 | margin-left: 1em;
510 | }
511 | div.footnote-label {
512 | display: inline-block;
513 | min-width: 1.7em;
514 | }
515 |
516 | div.option-list {
517 | border: 0.1em solid var(--border);
518 | }
519 | div.option-list-item {
520 | padding-left: 12em;
521 | padding-right: 0;
522 | padding-bottom: 0.3em;
523 | padding-top: 0.3em;
524 | }
525 | div.odd {
526 | background-color: var(--secondary-background);
527 | }
528 | div.option-list-label {
529 | margin-left: -11.5em;
530 | margin-right: 0em;
531 | min-width: 11.5em;
532 | display: inline-block;
533 | vertical-align: top;
534 | }
535 | div.option-list-description {
536 | width: calc(100% - 1em);
537 | padding-left: 1em;
538 | padding-right: 0;
539 | display: inline-block;
540 | }
541 |
542 | blockquote {
543 | font-size: 0.9em;
544 | font-style: italic;
545 | padding-left: 0.5em;
546 | margin-left: 0;
547 | border-left: 5px solid #bbc;
548 | }
549 |
550 | blockquote.markdown-quote {
551 | font-size: 0.9rem; /* use rem to avoid recursion */
552 | font-style: normal;
553 | }
554 |
555 | .pre, span.tok {
556 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;
557 | font-weight: 500;
558 | font-size: 0.85em;
559 | color: var(--text);
560 | background-color: var(--third-background);
561 | padding-left: 3px;
562 | padding-right: 3px;
563 | border-radius: 4px;
564 | }
565 |
566 | span.tok {
567 | border: 1px solid #808080;
568 | padding-bottom: 0.1em;
569 | margin-right: 0.2em;
570 | }
571 |
572 | .copyToClipBoard {
573 | position: relative;
574 | }
575 |
576 | pre {
577 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;
578 | color: var(--text);
579 | font-weight: 500;
580 | display: inline-block;
581 | box-sizing: border-box;
582 | min-width: 100%;
583 | padding: 0.5em;
584 | margin-top: 0.5em;
585 | margin-bottom: 0.5em;
586 | font-size: 0.85em;
587 | white-space: pre !important;
588 | overflow-y: hidden;
589 | overflow-x: visible;
590 | background-color: var(--secondary-background);
591 | border: 1px solid var(--border);
592 | -webkit-border-radius: 6px;
593 | -moz-border-radius: 6px;
594 | border-radius: 6px;
595 | }
596 |
597 | .copyToClipBoardBtn {
598 | visibility: hidden;
599 | position: absolute;
600 | width: 24px;
601 | border-radius: 4px;
602 | background-image: var(--clipboard-image);
603 | right: 5px;
604 | top: 13px;
605 | background-color: var(--secondary-background);
606 | padding: 11px;
607 | border: 0;
608 | }
609 |
610 | .copyToClipBoard:hover .copyToClipBoardBtn {
611 | visibility: visible;
612 | }
613 |
614 | .pre-scrollable {
615 | max-height: 340px;
616 | overflow-y: scroll; }
617 |
618 |
619 | /* Nim line-numbered tables */
620 | .line-nums-table {
621 | width: 100%;
622 | table-layout: fixed; }
623 |
624 | table.line-nums-table {
625 | border-radius: 4px;
626 | border: 1px solid #cccccc;
627 | background-color: ghostwhite;
628 | border-collapse: separate;
629 | margin-top: 15px;
630 | margin-bottom: 25px; }
631 |
632 | .line-nums-table tbody {
633 | border: none; }
634 |
635 | .line-nums-table td pre {
636 | border: none;
637 | background-color: transparent; }
638 |
639 | .line-nums-table td.blob-line-nums {
640 | width: 28px; }
641 |
642 | .line-nums-table td.blob-line-nums pre {
643 | color: #b0b0b0;
644 | -webkit-filter: opacity(75%);
645 | filter: opacity(75%);
646 | text-align: right;
647 | border-color: transparent;
648 | background-color: transparent;
649 | padding-left: 0px;
650 | margin-left: 0px;
651 | padding-right: 0px;
652 | margin-right: 0px; }
653 |
654 |
655 | table {
656 | max-width: 100%;
657 | background-color: transparent;
658 | margin-top: 0.5em;
659 | margin-bottom: 1.5em;
660 | border-collapse: collapse;
661 | border-color: var(--third-background);
662 | border-spacing: 0;
663 | font-size: 0.9em;
664 | }
665 |
666 | table th, table td {
667 | padding: 0px 0.5em 0px;
668 | border-color: var(--third-background);
669 | }
670 |
671 | table th {
672 | background-color: var(--third-background);
673 | border-color: var(--third-background);
674 | font-weight: bold; }
675 |
676 | table th.docinfo-name {
677 | background-color: transparent;
678 | text-align: right;
679 | }
680 |
681 | table tr:hover {
682 | background-color: var(--third-background); }
683 |
684 |
685 | /* rst2html default used to remove borders from tables and images */
686 | .borderless, table.borderless td, table.borderless th {
687 | border: 0; }
688 |
689 | table.borderless td, table.borderless th {
690 | /* Override padding for "table.docutils td" with "! important".
691 | The right padding separates the table cells. */
692 | padding: 0 0.5em 0 0 !important; }
693 |
694 | .admonition {
695 | padding: 0.3em;
696 | background-color: var(--secondary-background);
697 | border-left: 0.4em solid #7f7f84;
698 | margin-bottom: 0.5em;
699 | -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);
700 | -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);
701 | box-shadow: 0 5px 8px -6px rgba(0,0,0,.2);
702 | }
703 | .admonition-info {
704 | border-color: var(--info-background);
705 | }
706 | .admonition-info-text {
707 | color: var(--info-background);
708 | }
709 | .admonition-warning {
710 | border-color: var(--warning-background);
711 | }
712 | .admonition-warning-text {
713 | color: var(--warning-background);
714 | }
715 | .admonition-error {
716 | border-color: var(--error-background);
717 | }
718 | .admonition-error-text {
719 | color: var(--error-background);
720 | }
721 |
722 | .first {
723 | /* Override more specific margin styles with "! important". */
724 | margin-top: 0 !important; }
725 |
726 | .last, .with-subtitle {
727 | margin-bottom: 0 !important; }
728 |
729 | .hidden {
730 | display: none; }
731 |
732 | blockquote.epigraph {
733 | margin: 2em 5em; }
734 |
735 | dl.docutils dd {
736 | margin-bottom: 0.5em; }
737 |
738 | object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
739 | overflow: hidden; }
740 |
741 |
742 | div.figure {
743 | margin-left: 2em;
744 | margin-right: 2em; }
745 |
746 | div.footer, div.header {
747 | clear: both;
748 | text-align: center;
749 | color: #666;
750 | font-size: smaller; }
751 |
752 | div.footer {
753 | padding-top: 5em; }
754 |
755 | div.line-block {
756 | display: block;
757 | margin-top: 1em;
758 | margin-bottom: 1em; }
759 |
760 | div.line-block div.line-block {
761 | margin-top: 0;
762 | margin-bottom: 0;
763 | margin-left: 1.5em; }
764 |
765 | div.topic {
766 | margin: 2em; }
767 |
768 | div.search_results {
769 | background-color: var(--third-background);
770 | margin: 3em;
771 | padding: 1em;
772 | border: 1px solid #4d4d4d; }
773 |
774 | div#global-links ul {
775 | margin-left: 0;
776 | list-style-type: none; }
777 |
778 | div#global-links > simple-boot {
779 | margin-left: 3em; }
780 |
781 | hr.docutils {
782 | width: 75%; }
783 |
784 | img.align-left, .figure.align-left, object.align-left {
785 | clear: left;
786 | float: left;
787 | margin-right: 1em; }
788 |
789 | img.align-right, .figure.align-right, object.align-right {
790 | clear: right;
791 | float: right;
792 | margin-left: 1em; }
793 |
794 | img.align-center, .figure.align-center, object.align-center {
795 | display: block;
796 | margin-left: auto;
797 | margin-right: auto; }
798 |
799 | .align-left {
800 | text-align: left; }
801 |
802 | .align-center {
803 | clear: both;
804 | text-align: center; }
805 |
806 | .align-right {
807 | text-align: right; }
808 |
809 | /* reset inner alignment in figures */
810 | div.align-right {
811 | text-align: inherit; }
812 |
813 | p.attribution {
814 | text-align: right;
815 | margin-left: 50%; }
816 |
817 | p.caption {
818 | font-style: italic; }
819 |
820 | p.credits {
821 | font-style: italic;
822 | font-size: smaller; }
823 |
824 | p.label {
825 | white-space: nowrap; }
826 |
827 | p.rubric {
828 | font-weight: bold;
829 | font-size: larger;
830 | color: maroon;
831 | text-align: center; }
832 |
833 | p.topic-title {
834 | font-weight: bold; }
835 |
836 | pre.address {
837 | margin-bottom: 0;
838 | margin-top: 0;
839 | font: inherit; }
840 |
841 | pre.literal-block, pre.doctest-block, pre.math, pre.code {
842 | margin-left: 2em;
843 | margin-right: 2em; }
844 |
845 | pre.code .ln {
846 | color: grey; }
847 |
848 | /* line numbers */
849 | pre.code, code {
850 | background-color: #eeeeee; }
851 |
852 | pre.code .comment, code .comment {
853 | color: #5c6576; }
854 |
855 | pre.code .keyword, code .keyword {
856 | color: #3B0D06;
857 | font-weight: bold; }
858 |
859 | pre.code .literal.string, code .literal.string {
860 | color: #0c5404; }
861 |
862 | pre.code .name.builtin, code .name.builtin {
863 | color: #352b84; }
864 |
865 | pre.code .deleted, code .deleted {
866 | background-color: #DEB0A1; }
867 |
868 | pre.code .inserted, code .inserted {
869 | background-color: #A3D289; }
870 |
871 | span.classifier {
872 | font-style: oblique; }
873 |
874 | span.classifier-delimiter {
875 | font-weight: bold; }
876 |
877 | span.problematic {
878 | color: #b30000; }
879 |
880 | span.section-subtitle {
881 | /* font-size relative to parent (h1..h6 element) */
882 | font-size: 80%; }
883 |
884 | span.DecNumber {
885 | color: var(--number); }
886 |
887 | span.BinNumber {
888 | color: var(--number); }
889 |
890 | span.HexNumber {
891 | color: var(--number); }
892 |
893 | span.OctNumber {
894 | color: var(--number); }
895 |
896 | span.FloatNumber {
897 | color: var(--number); }
898 |
899 | span.Identifier {
900 | color: var(--identifier); }
901 |
902 | span.Keyword {
903 | font-weight: 600;
904 | color: var(--keyword); }
905 |
906 | span.StringLit {
907 | color: var(--literal); }
908 |
909 | span.LongStringLit {
910 | color: var(--literal); }
911 |
912 | span.CharLit {
913 | color: var(--literal); }
914 |
915 | span.EscapeSequence {
916 | color: var(--escapeSequence); }
917 |
918 | span.Operator {
919 | color: var(--operator); }
920 |
921 | span.Punctuation {
922 | color: var(--punctuation); }
923 |
924 | span.Comment, span.LongComment {
925 | font-style: italic;
926 | font-weight: 400;
927 | color: var(--comment); }
928 |
929 | span.RegularExpression {
930 | color: darkviolet; }
931 |
932 | span.TagStart {
933 | color: darkviolet; }
934 |
935 | span.TagEnd {
936 | color: darkviolet; }
937 |
938 | span.Key {
939 | color: #252dbe; }
940 |
941 | span.Value {
942 | color: #252dbe; }
943 |
944 | span.RawData {
945 | color: var(--raw-data); }
946 |
947 | span.Assembler {
948 | color: #252dbe; }
949 |
950 | span.Preprocessor {
951 | color: #252dbe; }
952 |
953 | span.Directive {
954 | color: #252dbe; }
955 |
956 | span.option {
957 | font-weight: bold;
958 | font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace;
959 | color: var(--option); }
960 |
961 | span.Prompt {
962 | font-weight: bold;
963 | color: red; }
964 |
965 | span.ProgramOutput {
966 | font-weight: bold;
967 | color: #808080; }
968 |
969 | span.program {
970 | font-weight: bold;
971 | color: var(--program);
972 | text-decoration: underline;
973 | text-decoration-color: var(--hint);
974 | text-decoration-thickness: 0.05em;
975 | text-underline-offset: 0.15em; }
976 |
977 | span.Command, span.Rule, span.Hyperlink,
978 | span.Label, span.Reference, span.Other {
979 | color: var(--other); }
980 |
981 | /* Pop type, const, proc, and iterator defs in nim def blocks */
982 | dt pre > span.Identifier, dt pre > span.Operator {
983 | color: var(--identifier);
984 | font-weight: 700; }
985 |
986 | dt pre > span.Keyword ~ span.Identifier, dt pre > span.Identifier ~ span.Identifier,
987 | dt pre > span.Operator ~ span.Identifier, dt pre > span.Other ~ span.Identifier {
988 | color: var(--identifier);
989 | font-weight: inherit; }
990 |
991 | /* Nim sprite for the footer (taken from main page favicon) */
992 | .nim-sprite {
993 | display: inline-block;
994 | width: 51px;
995 | height: 14px;
996 | background-position: 0 0;
997 | background-size: 51px 14px;
998 | -webkit-filter: opacity(50%);
999 | filter: opacity(50%);
1000 | background-repeat: no-repeat;
1001 | background-image: var(--nim-sprite-base64);
1002 | margin-bottom: 5px; }
1003 |
1004 | span.pragmadots {
1005 | /* Position: relative frees us up to make the dots
1006 | look really nice without fucking up the layout and
1007 | causing bulging in the parent container */
1008 | position: relative;
1009 | /* 1px down looks slightly nicer */
1010 | top: 1px;
1011 | padding: 2px;
1012 | background-color: var(--third-background);
1013 | border-radius: 4px;
1014 | margin: 0 2px;
1015 | cursor: pointer;
1016 | font-size: 0.8em; }
1017 |
1018 | span.pragmadots:hover {
1019 | background-color: var(--hint); }
1020 |
1021 | span.pragmawrap {
1022 | display: none; }
1023 |
1024 | span.attachedType {
1025 | display: none;
1026 | visibility: hidden; }
1027 |
--------------------------------------------------------------------------------
/examples/base64/decode.nim:
--------------------------------------------------------------------------------
1 | # todo try to reason if this is needed
2 | import std/base64
3 |
4 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
5 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
6 | if len == 0: return
7 | var
8 | copy = newString(len)
9 | decoded: string
10 | copyMem(addr copy[0], data, len)
11 | try: decoded = decode(copy) except: return
12 |
13 | when defined(fuzzSa):
14 | include libfuzzer/standalone
15 | else:
16 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
17 | proc NimMain() {.importc: "NimMain".}
18 | NimMain()
19 |
20 | proc mutate(data: ptr UncheckedArray[byte]; len, maxLen: int): int {.
21 | importc: "LLVMFuzzerMutate".}
22 |
23 | proc customMutator(data: ptr UncheckedArray[byte]; len, maxLen: int, seed: int64): int {.
24 | exportc: "LLVMFuzzerCustomMutator", raises: [].} =
25 | # Decompress the input data. If that fails, use a dummy value.
26 |
27 | var copy = newString(len)
28 | copyMem(addr copy[0], data, len)
29 | var decoded = try: decode(copy) except: "hi"
30 | # Mutate the decoded data with `libFuzzer`'s default mutator. Expand
31 | # the `decompressed` seq's for inserting mutations via `grow`.
32 | let oldLen = decoded.len
33 | decoded.setLen(oldLen*2)
34 | let newDecodedLen = mutate(cast[ptr UncheckedArray[byte]](addr decoded[0]),
35 | oldLen, decoded.len)
36 | # Recompress the mutated data.
37 | var encoded = encode(decoded.toOpenArray(0, newDecodedLen-1))
38 | # Copy the recompressed mutated data into `data` and return the new length.
39 | result = min(maxLen, encoded.len)
40 | copyMem(addr data[0], addr encoded[0], result)
41 | #for i in 0..", nodecl.}
10 |
11 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
12 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
13 | let cLen = len div sizeof(float)
14 | if cLen == 0: return
15 | var copy = newSeq[float](cLen)
16 | copyMem(addr copy[0], data, copy.len * sizeof(float))
17 |
18 | let res = sum(copy)
19 | if isNaN(res):
20 | quitOrDebug()
21 |
22 | proc customMutator(data: ptr UncheckedArray[byte], len, maxLen: int, seed: int64): int {.
23 | exportc: "LLVMFuzzerCustomMutator", raises: [].} =
24 |
25 | let cLen = len div sizeof(float)
26 | if cLen == 0:
27 | var tmp = @[1.0, 3, 3, 7] # Use a dummy value
28 | result = tmp.len * sizeof(float)
29 | copyMem(data, addr tmp[0], result)
30 | return
31 | var copy = newSeq[float](cLen)
32 | copyMem(addr copy[0], data, copy.len * sizeof(float))
33 | var gen = initRand(seed)
34 |
35 | proc rfp(gen: var Rand): float =
36 | case gen.rand(10)
37 | of 0:
38 | result = NaN
39 | of 1:
40 | result = minimumPositiveValue(float)
41 | of 2:
42 | result = maximumPositiveValue(float)
43 | of 3:
44 | result = -minimumPositiveValue(float)
45 | of 4:
46 | result = -maximumPositiveValue(float)
47 | of 5:
48 | result = epsilon(float)
49 | of 6:
50 | result = -epsilon(float)
51 | of 7:
52 | result = Inf
53 | of 8:
54 | result = -Inf
55 | of 9:
56 | result = 0.0
57 | else:
58 | result = gen.rand(-1.0..1.0)
59 |
60 | case gen.rand(3)
61 | of 0: # Change element
62 | if copy.len > 0:
63 | copy[gen.rand(0.. 0:
68 | discard copy.pop
69 | else: # Shuffle elements
70 | gen.shuffle(copy)
71 |
72 | result = copy.len * sizeof(float)
73 | if result <= maxLen:
74 | copyMem(data, addr copy[0], result)
75 | else:
76 | result = 0
77 |
78 | proc customCrossOver(data1: ptr UncheckedArray[byte], len1: int,
79 | data2: ptr UncheckedArray[byte], len2: int, res: ptr UncheckedArray[byte],
80 | maxResLen: int, seed: int64): int {.
81 | exportc: "LLVMFuzzerCustomCrossOver", raises: [].} =
82 |
83 | let cLen1 = len1 div sizeof(float)
84 | if cLen1 == 0: return
85 | var copy1 = newSeq[float](cLen1)
86 | copyMem(addr copy1[0], data1, copy1.len * sizeof(float))
87 |
88 | let cLen2 = len2 div sizeof(float)
89 | if cLen2 == 0: return
90 | var copy2 = newSeq[float](cLen2)
91 | copyMem(addr copy2[0], data2, copy2.len * sizeof(float))
92 |
93 | let len = min(copy1.len, min(copy2.len, maxResLen div sizeof(float)))
94 | if len == 0: return
95 | var buf = newSeq[float](len)
96 | copyMem(addr buf[0], res, buf.len * sizeof(float))
97 |
98 | var gen = initRand(seed)
99 | for i in 0 ..< buf.len:
100 | buf[i] = if gen.rand(1.0) <= 0.5: copy1[i] else: copy2[i]
101 |
102 | result = buf.len * sizeof(float)
103 | assert result <= maxResLen
104 | copyMem(res, addr buf[0], result)
105 |
--------------------------------------------------------------------------------
/examples/imgread.nim:
--------------------------------------------------------------------------------
1 | # https://www.youtube.com/watch?v=hFva8kJQwnc
2 | # a vulnerable C program to explain common vulnerability types fuzz with libfuzzer
3 | # ./imgread -fork=1 -ignore_crashes=1
4 |
5 | type
6 | Image = object
7 | header: array[4, char]
8 | width, height: cint
9 | data: array[10, char]
10 |
11 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
12 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
13 | if len < sizeof(Image):
14 | return 0
15 | let img = cast[ptr Image](data)
16 | # integer overflow 0x7FFFFFFF+1=0
17 | # 0x7FFFFFFF+2 = 1
18 | # will cause very large/small memory allocation.
19 | let size1 = img.width + img.height
20 | var buff1 = cast[cstring](alloc(size1))
21 | # heap buffer overflow
22 | copyMem(buff1, addr img.data, sizeof(img.data))
23 | dealloc(buff1)
24 | # double dealloc
25 | if size1 div 3 == 0:
26 | dealloc(buff1)
27 | else:
28 | # use after dealloc
29 | if size1 div 20 == 0:
30 | buff1[0] = 'a'
31 | # integer underflow 0-1=-1
32 | # negative so will cause very large memory allocation
33 | let size2 = img.width - img.height + 100
34 | # echo("Size1: ", size1)
35 | let buff2 = cast[cstring](alloc(size2))
36 | # heap buffer overflow
37 | copyMem(buff2, addr img.data, sizeof(img.data))
38 | # divide by zero
39 | let size3 = img.width div img.height
40 | # echo("Size2: ", size3)
41 | var buff3: array[10, char]
42 | var buff4 = cast[cstring](alloc(size3))
43 | copyMem(buff4, addr img.data, sizeof(img.data))
44 | # stack OOBR read bytes past buffer
45 | let OOBR_stack = buff3[size3]
46 | let OOBR_heap = buff4[size1]
47 | # stack OOBW write bytes past buffer
48 | buff3[size3] = 'c'
49 | buff4[size1] = 'c'
50 | if size3 div 5 == 0:
51 | # memory leak here
52 | buff4 = nil
53 | else:
54 | dealloc(buff4)
55 | dealloc(buff2)
56 |
--------------------------------------------------------------------------------
/experiments/altapi.nim:
--------------------------------------------------------------------------------
1 | # Alternative to strict .raises: []
2 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
3 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
4 |
5 | template trap(body: untyped) =
6 | try:
7 | body
8 | finally: {.emit: "nimTestErrorFlag();".}
9 |
10 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
11 | exportc: "LLVMFuzzerTestOneInput", trap.} =
12 | if true:
13 | raise newException(ValueError, "my my my")
14 |
--------------------------------------------------------------------------------
/experiments/api_exp.nim:
--------------------------------------------------------------------------------
1 | proc LLVMFuzzerMutate(data: ptr UncheckedArray[byte], len, maxLen: int): int {.
2 | importc.}
3 | proc mutate(data: var openarray[byte], oldLen: int): int {.inline.} =
4 | LLVMFuzzerMutate(cast[ptr UncheckedArray[byte]](data), oldLen, data.len)
5 | proc customMutator(data: var openarray[byte], oldLen: int, seed: int64): int {.
6 | exportc: "LLVMFuzzerCustomMutator".} = discard "to implement"
7 | proc LLVMFuzzerCustomMutator(data: ptr UncheckedArray[byte], len, maxLen: int, seed: int64): int {.
8 | exportc.} = customMutator(toOpenArray(data, 0, maxLen-1), len)
9 | #Error: type mismatch: got
10 | #but expected one of:
11 | #proc customMutator(data: var openArray[byte]; oldLen: int; seed: int64): int
12 | #first type mismatch at position: 1
13 | #required type for data: var openArray[byte]
14 | #but expression 'toOpenArray(data, 0, maxLen - 1)' is immutable, not 'var'
15 |
16 | #expression: customMutator(toOpenArray(data, 0, maxLen - 1), len)
17 |
--------------------------------------------------------------------------------
/experiments/memstreams.nim:
--------------------------------------------------------------------------------
1 | import std/streams
2 |
3 | type
4 | MemStream* = ref MemStreamObj
5 | ## A stream that encapsulates a openarray[byte].
6 | MemStreamObj = object of StreamObj
7 | data: ptr UncheckedArray[byte]
8 | len, pos: int
9 |
10 | proc `=sink`*(dest: var MemStreamObj; source: MemStreamObj) {.error.}
11 | proc `=copy`*(dest: var MemStreamObj; source: MemStreamObj) {.error.}
12 |
13 | proc msAtEnd(s: Stream): bool =
14 | let s = MemStream(s)
15 | result = s.pos >= s.len
16 |
17 | proc msSetPosition(s: Stream, pos: int) =
18 | let s = MemStream(s)
19 | s.pos = clamp(pos, 0, s.len)
20 |
21 | proc msGetPosition(s: Stream): int =
22 | let s = MemStream(s)
23 | result = s.pos
24 |
25 | proc msReadData(s: Stream, buffer: pointer, bufLen: int): int =
26 | let s = MemStream(s)
27 | result = min(bufLen, s.len - s.pos)
28 | if result > 0:
29 | copyMem(buffer, addr s.data[s.pos], result)
30 | inc(s.pos, result)
31 | else:
32 | result = 0
33 |
34 | proc msPeekData(s: Stream, buffer: pointer, bufLen: int): int =
35 | let s = MemStream(s)
36 | result = min(bufLen, s.len - s.pos)
37 | if result > 0:
38 | copyMem(buffer, addr s.data[s.pos], result)
39 | else:
40 | result = 0
41 |
42 | proc msWriteData(s: Stream, buffer: pointer, bufLen: int) =
43 | var s = MemStream(s)
44 | if bufLen <= 0:
45 | return
46 | if s.pos + bufLen > s.len:
47 | raise newException(IOError, "cannot write to stream")
48 | copyMem(addr(s.data[s.pos]), buffer, bufLen)
49 | inc(s.pos, bufLen)
50 |
51 | proc newMemStream*(s: openarray[byte]): MemStream =
52 | result = MemStream(
53 | data: cast[ptr UncheckedArray[byte]](s),
54 | len: s.len,
55 | pos: 0,
56 | closeImpl: nil,
57 | atEndImpl: msAtEnd,
58 | setPositionImpl: msSetPosition,
59 | getPositionImpl: msGetPosition,
60 | readDataStrImpl: nil,
61 | readDataImpl: msReadData,
62 | peekDataImpl: msPeekData,
63 | writeDataImpl: msWriteData
64 | )
65 |
66 | proc newMemStream*(data: ptr UncheckedArray[byte], len: int): MemStream =
67 | result = MemStream(
68 | data: data,
69 | len: len,
70 | pos: 0,
71 | closeImpl: nil,
72 | atEndImpl: msAtEnd,
73 | setPositionImpl: msSetPosition,
74 | getPositionImpl: msGetPosition,
75 | readDataStrImpl: nil,
76 | readDataImpl: msReadData,
77 | peekDataImpl: msPeekData,
78 | writeDataImpl: msWriteData
79 | )
80 |
--------------------------------------------------------------------------------
/experiments/nfpsum.nim:
--------------------------------------------------------------------------------
1 | # https://rigtorp.se/fuzzing-floating-point-code/
2 | import std/[random, fenv, math, streams], bingo, memstreams
3 |
4 | proc sum(x: openArray[float]): float =
5 | result = 0.0
6 | for b in items(x):
7 | result = if isNaN(b): result else: result + b
8 |
9 | proc quitOrDebug() {.noreturn, importc: "abort", header: "", nodecl.}
10 |
11 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
12 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
13 | var copy: seq[float]
14 | try:
15 | let str = newMemStream(data, len)
16 | loadBin(str, copy)
17 | except:
18 | return
19 | if copy.len == 0: return
20 | let res = sum(copy)
21 | if isNaN(res):
22 | quitOrDebug()
23 |
24 | when defined(fuzzSa):
25 | include libfuzzer/standalone
26 | else:
27 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
28 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
29 |
30 | proc customMutator(data: ptr UncheckedArray[byte], len, maxLen: int, seed: int64): int {.
31 | exportc: "LLVMFuzzerCustomMutator".} =
32 |
33 | proc randFloat(gen: var Rand): float =
34 | case gen.rand(10)
35 | of 0:
36 | result = NaN
37 | of 1:
38 | result = minimumPositiveValue(float)
39 | of 2:
40 | result = maximumPositiveValue(float)
41 | of 3:
42 | result = -minimumPositiveValue(float)
43 | of 4:
44 | result = -maximumPositiveValue(float)
45 | of 5:
46 | result = epsilon(float)
47 | of 6:
48 | result = -epsilon(float)
49 | of 7:
50 | result = Inf
51 | of 8:
52 | result = -Inf
53 | of 9:
54 | result = 0.0
55 | else:
56 | result = gen.rand(-1.0..1.0)
57 |
58 | var copy: seq[float]
59 | try:
60 | let readStr = newMemStream(data, len)
61 | loadBin(readStr, copy)
62 | except:
63 | let writeStr = newMemStream(data, maxLen)
64 | writeStr.storeBin(@[1.0, 2, 3, 4])
65 | result = writeStr.getPosition()
66 |
67 | if copy.len == 0: return
68 | var gen = initRand(seed)
69 | case gen.rand(3)
70 | of 0: # Change element
71 | if copy.len > 0:
72 | copy[gen.rand(0.. 0:
77 | discard copy.pop
78 | else: # Shuffle elements
79 | gen.shuffle(copy)
80 |
81 | result = copy.byteSize
82 | if result <= maxLen:
83 | let writeStr = newMemStream(data, maxLen)
84 | writeStr.storeBin(copy)
85 | else:
86 | result = len
87 |
88 | proc customCrossOver(data1: ptr UncheckedArray[byte], len1: int,
89 | data2: ptr UncheckedArray[byte], len2: int, res: ptr UncheckedArray[byte],
90 | maxResLen: int, seed: int64): int {.
91 | exportc: "LLVMFuzzerCustomCrossOver".} =
92 |
93 | var copy1: seq[float]
94 | try:
95 | let readStr1 = newMemStream(data1, len1)
96 | loadBin(readStr1, copy1)
97 | except:
98 | return
99 |
100 | var copy2: seq[float]
101 | try:
102 | let readStr2 = newMemStream(data2, len2)
103 | loadBin(readStr2, copy2)
104 | except:
105 | return
106 |
107 | let len = min(copy1.len, min(copy2.len, maxResLen div sizeof(float)))
108 | if len == 0: return
109 | var buf = newSeq[float](len)
110 |
111 | var gen = initRand(seed)
112 | for i in 0 ..< buf.len:
113 | buf[i] = if gen.rand(1.0) <= 0.5: copy1[i]
114 | else: copy2[i]
115 |
116 | result = buf.byteSize
117 | if result <= maxResLen:
118 | let writeStr = newMemStream(res, maxResLen)
119 | writeStr.storeBin(buf)
120 | else:
121 | result = len
122 |
--------------------------------------------------------------------------------
/experiments/nfpsum.nims:
--------------------------------------------------------------------------------
1 | --cc: clang
2 | --debugger: native # ignored
3 | #--header
4 | --define: noSignalHandler
5 | --define: useMalloc
6 | when not defined(fuzzSa):
7 | --noMain: on
8 | --passC: "-fsanitize=fuzzer"
9 | --passL: "-fsanitize=fuzzer"
10 | --passC: "-fsanitize=address,undefined"
11 | --passL: "-fsanitize=address,undefined"
12 | --path: "../"
13 |
--------------------------------------------------------------------------------
/experiments/tstructure.nim:
--------------------------------------------------------------------------------
1 | import std/streams, bingo, memstreams, chroma
2 |
3 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
4 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
5 |
6 | proc fuzzTarget(color: ColorRgb) =
7 | let hsl = color.asHsl
8 | let rgb = hsl.asRgb
9 | # This should be true for all RGB -> HSL -> RGB conversions!
10 | doAssert color == rgb
11 |
12 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
13 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
14 | var target: ColorRgb
15 | try:
16 | let str = newReadStream(data, len)
17 | loadBin(str, target)
18 | except:
19 | return
20 | fuzzTarget(target)
21 |
--------------------------------------------------------------------------------
/libfuzzer.nimble:
--------------------------------------------------------------------------------
1 | # Package
2 |
3 | version = "0.1.0"
4 | author = "Antonis Geralis"
5 | description = "Thin interface for libFuzzer, an in-process, coverage-guided, evolutionary fuzzing engine."
6 | license = "MIT"
7 |
8 | # Deps
9 |
10 | requires "nim >= 1.0.0"
11 |
12 | import os
13 |
14 | const
15 | ProjectUrl = "https://github.com/planetis-m/libfuzzer"
16 | PkgDir = thisDir().quoteShell
17 | DocsDir = PkgDir / "docs"
18 |
19 | task docs, "Generate documentation":
20 | # https://nim-lang.github.io/Nim/docgen.html
21 | withDir(PkgDir):
22 | let tmp = "fuzztarget"
23 | let doc = DocsDir / (tmp & ".html")
24 | let src = "libfuzzer" / (tmp & ".nim")
25 | # Generate the docs for {src}
26 | exec("nim doc --verbosity:0 --git.url:" & ProjectUrl &
27 | " --git.devel:master --git.commit:master --out:" & doc & " " & src)
28 |
--------------------------------------------------------------------------------
/libfuzzer/fuzztarget.nim:
--------------------------------------------------------------------------------
1 | ## NOTE: the libFuzzer interface is thin and in the majority of cases
2 | ## all you need is to define the procedure `testOneInput` in your file.
3 |
4 | proc testOneInput*(data: ptr UncheckedArray[byte], len: int): cint {.
5 | exportc: "LLVMFuzzerTestOneInput".} =
6 | ## Mandatory user-provided target procedure.
7 | ## Executes the code under test with `data` as the input.
8 | ## libFuzzer will invoke this procedure *many* times with different inputs.
9 | ## Must return 0.
10 | discard "to implement"
11 |
12 | when defined(fuzzSa) or defined(nimdoc):
13 | include standalone
14 | when not defined(fuzzSa) or defined(nimdoc):
15 | proc initialize*(): cint {.exportc: "LLVMFuzzerInitialize".} =
16 | ## Initialize Nim's internals, which is done calling a NimMain function.
17 | # https://nim-lang.github.io/Nim/backends.html#interfacing-backend-code-calling-nim
18 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
19 |
20 | proc mutate*(data: ptr UncheckedArray[byte], len, maxLen: int): int {.
21 | importc: "LLVMFuzzerMutate".}
22 | ## Experimental, may go away in future.
23 | ## libFuzzer-provided procedure to be used inside `customMutator`.
24 | ## Mutates raw data in `data[0..`_
19 |
20 | Clang Sanitizers
21 | ================
22 |
23 | Sanitizers are compiler build-in error detectors with relatively small runtime
24 | cost. Clang has:
25 |
26 | - `AddressSanitizer `_ - use-after-free, double-free, ...
27 | - `MemorySanitizer `_ - uninitialized reads
28 | - `UndefinedBehaviourSanitizer `_ - overflows, divide by zero, ...
29 | - `ThreadSanitizer `_ - data races
30 |
31 | For more information watch the talk *Sanitize your C++ code* [4]_
32 | There are demos at the `tests `_ directory.
33 |
34 | Example
35 | =======
36 |
37 | In 95% of cases all you need is to define the procedure ``testOneInput`` in your file.
38 |
39 |
40 | .. code-block:: nim
41 |
42 | proc fuzzMe(data: openarray[byte]): bool =
43 | result = data.len >= 3 and
44 | data[0].char == 'F' and
45 | data[1].char == 'U' and
46 | data[2].char == 'Z' and
47 | data[3].char == 'Z' # :‑<
48 |
49 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
50 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
51 |
52 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
53 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
54 | result = 0
55 | discard fuzzMe(data.toOpenArray(0, len-1))
56 |
57 |
58 | Compile with:
59 |
60 | .. code-block::
61 |
62 | $ nim c --cc:clang -t:"-fsanitize=fuzzer,address,undefined" -l:"-fsanitize=fuzzer,address,undefined" -d:nosignalhandler --nomain:on -g tfuzz.nim
63 |
64 |
65 | Coverage report
66 | ===============
67 |
68 | Use `Clang Coverage `_ to visualize and study your code coverage.
69 |
70 | - Include the `standalone `_ main procedure for fuzz targets.
71 | - Follow the instructions given at the `test coverage `_ example.
72 | - When running the executable, pass as parameter a list of test units.
73 |
74 | Structure-Aware Fuzzing
75 | =======================
76 |
77 | But the lack of an input grammar can also result in inefficient fuzzing
78 | for complicated input types, where any traditional mutation (e.g. bit
79 | flipping) leads to an invalid input rejected by the target API in the
80 | early stage of parsing. With some additional effort, however, libFuzzer
81 | can be turned into a grammar-aware (i.e. structure-aware) fuzzing engine
82 | for a specific input type.
83 |
84 | —*Structure-Aware Fuzzing with libFuzzer* [5]_
85 |
86 | Take a look at the snappy compression `example `_
87 | and ` `_
88 |
89 | Installation
90 | ============
91 |
92 | - Copy the files ``libfuzzer/fuzztarget.{nim,nims}``, ``libfuzzer/standalone.nim`` at your testing directory.
93 | - Fill in the implementations of the exported procedures.
94 | - Compile and run with an empty corpus directory as an argument.
95 |
96 | Presentations
97 | =============
98 |
99 | .. [#] Jonathan Metzman `Fuzzing 101 `_
100 | .. [#] Kostya Serebryany `Fuzz or lose... `_
101 | .. [#] Kostya Serebryany `Sanitize your C++ code `_
102 |
103 | Further Readings
104 | ================
105 |
106 | .. [#] `libFuzzer Tutorial `_
107 | .. [#] `Structure-Aware Fuzzing with libFuzzer `_
108 | .. [#] `Efficient Fuzzing Guide `_
109 |
--------------------------------------------------------------------------------
/tests/shadowmem.nim:
--------------------------------------------------------------------------------
1 | import std/strformat
2 |
3 | {.pragma: noASan, codegenDecl: "__attribute__((no_sanitize_address)) $# $#$#".}
4 |
5 | proc getShadowMapping(shadowScale, shadowOffset: ptr int) {.header:
6 | "sanitizer/asan_interface.h", importc: "__asan_get_shadow_mapping".}
7 |
8 | var
9 | shadowMemoryScale: int
10 | shadowMemoryOffset: int
11 |
12 | getShadowMapping(addr shadowMemoryScale, addr shadowMemoryOffset)
13 |
14 | proc printShadowMemoryImpl(address: pointer, filename: string, line: int) {.noASan.} =
15 | let shadowMemory = cast[ptr UncheckedArray[uint8]](
16 | cast[uint](address) shr shadowMemoryScale + shadowMemoryOffset.uint)
17 | stdout.write(&"Shadow Memory at {filename}:{line}\n")
18 | stdout.write(&"{cast[ByteAddress](address):#x}: {shadowMemory[0]:02x} {shadowMemory[1]:02x} {shadowMemory[2]:02x} {shadowMemory[3]:02x} {shadowMemory[4]:02x} {shadowMemory[5]:02x} {shadowMemory[6]:02x} {shadowMemory[7]:02x}\n")
19 |
20 | template printShadowMemory*(address: untyped) =
21 | let (filename, line, _) = instantiationInfo()
22 | printShadowMemoryImpl(address, filename, line)
23 |
--------------------------------------------------------------------------------
/tests/tasan.nim:
--------------------------------------------------------------------------------
1 | # --panics:on --gc:arc -d:useMalloc -t:"-fsanitize=address,undefined"
2 | # -l:"-fsanitize=address,undefined" -d:nosignalhandler -d:danger -g
3 |
4 | import os, strutils, algorithm
5 |
6 | var
7 | data: array[1000, int]
8 |
9 | proc main: int =
10 | fill data, -1
11 | let idx = parseInt(paramStr(1))
12 | result = data[idx + 100]
13 |
14 | discard main()
15 |
16 | #[
17 | const
18 | arrLen = 1_000
19 |
20 | type
21 | Array = object
22 | data: ptr array[arrLen, int]
23 |
24 | proc `=destroy`*(x: var Array) =
25 | if x.data != nil:
26 | dealloc(x.data)
27 |
28 | proc init(x: var Array) =
29 | x.data = cast[typeof(x.data)](alloc(arrLen * sizeof(int)))
30 |
31 | proc main =
32 | var
33 | arr: Array
34 | init arr
35 | `=destroy`(arr)
36 | echo arr.data[0]
37 |
38 | main()]#
39 |
--------------------------------------------------------------------------------
/tests/tcmdline.nim:
--------------------------------------------------------------------------------
1 | # os.paramCount/paramStr not supported
2 | import std/parseopt
3 |
4 | proc quitOrDebug() {.noreturn, importc: "abort", header: "", nodecl.}
5 |
6 | var
7 | cmdCountPtr: ptr cint
8 | cmdLinePtr: ptr cstringArray
9 |
10 | proc initialize(argc: ptr cint; argv: ptr cstringArray): cint {.exportc: "LLVMFuzzerInitialize".} =
11 | cmdCountPtr = argc
12 | cmdLinePtr = argv
13 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
14 |
15 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
16 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
17 | let cmdline = cstringArrayToSeq(cmdLinePtr[], cmdCountPtr[])
18 | var p = initOptParser(cmdline)
19 | for kind, key, val in p.getopt():
20 | case kind
21 | of cmdLongOption, cmdShortOption:
22 | case key
23 | of "abort", "a": quitOrDebug()
24 | else: discard
25 |
--------------------------------------------------------------------------------
/tests/tcov.nim:
--------------------------------------------------------------------------------
1 | # Compile with the following flags
2 | # For explanation: https://nim-lang.github.io/Nim/nimc.html
3 | # --panics:on --gc:arc -d:useMalloc --cc:clang -t:"-fprofile-instr-generate -fcoverage-mapping"
4 | # -l:"-fprofile-instr-generate -fcoverage-mapping" -d:danger
5 | # Run the executable
6 | # llvm-profdata merge -sparse=true default.profraw -o default.profdata
7 | # llvm-cov show -instr-profile=default.profdata -name=foo_tcov ./tcov
8 | # Missing:
9 | # Output Nim source code
10 | # Specify a Nim demangler with -Xdemangler
11 | template bar(x: untyped): untyped = x or x
12 |
13 | proc foo[T](x: T) =
14 | for i in 0..<10:
15 | discard bar i
16 |
17 | proc main =
18 | foo[int32](0)
19 | foo[float32](0)
20 |
21 | main()
22 |
--------------------------------------------------------------------------------
/tests/tcrossover.nim:
--------------------------------------------------------------------------------
1 | import std/[hashes, strformat]
2 |
3 | const
4 | Separator = "-########-"
5 | Target = "A-########-B"
6 |
7 | var
8 | sink: int
9 | printed: int
10 |
11 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
12 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
13 |
14 | proc testOneInput*(data: ptr UncheckedArray[byte], len: int): cint {.
15 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
16 | result = 0
17 | const targetHash = hash(Target)
18 | var strHash = hash(data.toOpenArray(0, len-1))
19 | # Ensure we have 'A' and 'B' in the corpus.
20 | if len == 1 and data[0] == byte'A':
21 | inc(sink)
22 | if len == 1 and data[0] == byte'B':
23 | dec(sink)
24 | if targetHash == strHash:
25 | quit "BINGO; Found the target, exiting"
26 |
27 | proc customCrossOver(data1: ptr UncheckedArray[byte], len1: int,
28 | data2: ptr UncheckedArray[byte], len2: int, res: ptr UncheckedArray[byte],
29 | maxResLen: int, seed: int64): int {.
30 | exportc: "LLVMFuzzerCustomCrossOver".} =
31 | const separatorLen = len(Separator)
32 | if printed < 32:
33 | stderr.write &"In customCrossover {len1} {len2}\n"
34 | inc(printed)
35 | result = len1 + len2 + separatorLen
36 | if result > maxResLen:
37 | return 0
38 | for i in 0..", nodecl.}
2 |
3 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
4 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
5 |
6 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
7 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
8 | if true:
9 | raise newException(RangeDefect, "my my my")
10 |
11 | proc customMutator(data: ptr UncheckedArray[byte]; len, maxLen: int, seed: int64): int {.
12 | exportc: "LLVMFuzzerCustomMutator".} =
13 | try:
14 | discard
15 | except:
16 | echo getCurrentExceptionMsg()
17 | quitOrDebug()
18 |
--------------------------------------------------------------------------------
/tests/tfuzz.nim:
--------------------------------------------------------------------------------
1 | # https://llvm.org/docs/LibFuzzer.html
2 |
3 | # --panics:on --gc:arc -d:useMalloc --cc:clang -t:"-fsanitize=fuzzer,address,undefined"
4 | # -l:"-fsanitize=fuzzer,address,undefined" -d:nosignalhandler --nomain:on -d:danger -g
5 |
6 | # objdump --no-show-raw-insn -drwlS -M intel --start-address=0x
7 |
8 | proc fuzzMe(data: openarray[byte]): bool =
9 | result = data.len >= 3 and
10 | data[0].char == 'F' and
11 | data[1].char == 'U' and
12 | data[2].char == 'Z' and
13 | data[3].char == 'Z' # :‑<
14 |
15 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
16 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
17 |
18 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
19 | exportc: "LLVMFuzzerTestOneInput", raises: [].} =
20 | result = 0
21 | discard fuzzMe(data.toOpenArray(0, len-1))
22 |
--------------------------------------------------------------------------------
/tests/tmem.nim:
--------------------------------------------------------------------------------
1 | # Contrived example of ASan poisoning, use `-d:useMalloc`
2 | # https://github.com/mcgov/asan_alignment_example
3 | import shadowmem
4 |
5 | proc poisonMem(region: pointer, size: int) {.header:
6 | "sanitizer/asan_interface.h", importc: "ASAN_POISON_MEMORY_REGION".}
7 |
8 | proc unpoisonMem(region: pointer, size: int) {.header:
9 | "sanitizer/asan_interface.h", importc: "ASAN_UNPOISON_MEMORY_REGION".}
10 |
11 | template `+!`(p: pointer, s: int): pointer =
12 | cast[pointer](cast[int](p) +% s)
13 |
14 | type
15 | Point = object
16 | x: float32
17 |
18 | var
19 | points = newSeq[Point](5)
20 | # Print the address
21 | printShadowMemory(addr points[1])
22 | # Poison the entire seq
23 | poisonMem(addr points[0], points.len * sizeof(Point))
24 | # Create a Point
25 | unpoisonMem(addr points[1], sizeof(Point))
26 | points[1] = Point(x: 1)
27 | echo points[1]
28 | # Pretend to destroy `points[1]`
29 | poisonMem(addr points[1], sizeof(Point))
30 | printShadowMemory(addr points[1])
31 | echo points[1]
32 |
--------------------------------------------------------------------------------
/tests/traise.nim:
--------------------------------------------------------------------------------
1 | proc quitOrDebug() {.noreturn, importc: "abort", header: "", nodecl.}
2 |
3 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
4 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
5 |
6 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
7 | exportc: "LLVMFuzzerTestOneInput".} = #, raises: [].} =
8 | if true:
9 | raise newException(ValueError, "my my my")
10 |
11 | proc customMutator(data: ptr UncheckedArray[byte]; len, maxLen: int, seed: int64): int {.
12 | exportc: "LLVMFuzzerCustomMutator".} =
13 | try:
14 | discard
15 | except:
16 | echo getCurrentExceptionMsg()
17 | quitOrDebug()
18 |
--------------------------------------------------------------------------------
/tests/tstring.nim:
--------------------------------------------------------------------------------
1 | # https://www.moritz.systems/blog/an-introduction-to-llvm-libfuzzer/
2 | # Compile with and without asan builtin interceptors "-fsanitize=fuzzer(,address)"
3 | # ./tstring -runs=1000000
4 |
5 | proc quitOrDebug() {.noreturn, importc: "abort", header: "", nodecl.}
6 |
7 | proc initialize(): cint {.exportc: "LLVMFuzzerInitialize".} =
8 | {.emit: "N_CDECL(void, NimMain)(void); NimMain();".}
9 |
10 | proc testOneInput(data: ptr UncheckedArray[byte], len: int): cint {.
11 | exportc: "LLVMFuzzerTestOneInput".} =
12 | if len >= 7:
13 | var copy = newString(6)
14 | copyMem(cstring(copy), cast[cstring](data), copy.len)
15 | if copy == "qwerty":
16 | stderr.write("BINGO\n")
17 | quitOrDebug()
18 |
--------------------------------------------------------------------------------
/tests/ttsan.nim:
--------------------------------------------------------------------------------
1 | # https://github.com/google/sanitizers/wiki/ThreadSanitizerPopularDataRaces
2 | # pbl benign
3 | # --threads:on --panics:on --gc:arc -d:useMalloc -t:"-fsanitize=thread"
4 | # -l:"-fsanitize=thread" -d:nosignalhandler -d:danger -g
5 | # TSAN_OPTIONS="force_seq_cst_atomics=1"
6 | import std/[atomics, os]
7 |
8 | const
9 | delay = 1_000
10 |
11 | var
12 | thread: Thread[void]
13 | proceed: Atomic[bool]
14 | bArrived = false
15 |
16 | proc routine =
17 | var count = 0
18 | while true:
19 | if count mod delay == 0 and proceed.load(moRelaxed):
20 | break
21 | cpuRelax()
22 | inc count
23 | doAssert bArrived
24 |
25 | proc testNotify =
26 | createThread(thread, routine)
27 | sleep 10
28 | bArrived = true
29 | proceed.store(true, moRelaxed)
30 | joinThread thread
31 |
32 | testNotify()
33 |
--------------------------------------------------------------------------------