diff options
| -rw-r--r-- | .github/workflows/ci.yml | 3 | ||||
| -rw-r--r-- | Makefile | 5 | ||||
| -rw-r--r-- | src/Makefile | 27 | ||||
| -rw-r--r-- | src/fuzz_script.cpp | 59 |
4 files changed, 88 insertions, 6 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9efd66f..ba8074c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml | |||
| @@ -28,6 +28,9 @@ jobs: | |||
| 28 | - name: Run P2P message fuzz smoke | 28 | - name: Run P2P message fuzz smoke |
| 29 | run: make fuzz-net-message-smoke | 29 | run: make fuzz-net-message-smoke |
| 30 | 30 | ||
| 31 | - name: Run script fuzz smoke | ||
| 32 | run: make fuzz-script-smoke | ||
| 33 | |||
| 31 | windows-ucrt64: | 34 | windows-ucrt64: |
| 32 | name: Windows UCRT64 selftests | 35 | name: Windows UCRT64 selftests |
| 33 | runs-on: windows-latest | 36 | runs-on: windows-latest |
| @@ -194,6 +194,9 @@ tests: | |||
| 194 | fuzz-net-message-smoke: deps-linux | 194 | fuzz-net-message-smoke: deps-linux |
| 195 | $(MAKE) -C src -f Makefile fuzz-net-message-smoke | 195 | $(MAKE) -C src -f Makefile fuzz-net-message-smoke |
| 196 | 196 | ||
| 197 | fuzz-script-smoke: deps-linux | ||
| 198 | $(MAKE) -C src -f Makefile fuzz-script-smoke | ||
| 199 | |||
| 197 | checksums: | 200 | checksums: |
| 198 | ./scripts/make-release-checksums.sh | 201 | ./scripts/make-release-checksums.sh |
| 199 | 202 | ||
| @@ -204,5 +207,5 @@ verify-release: | |||
| 204 | ./scripts/verify-release.sh $(if $(TAG),$(TAG),latest) | 207 | ./scripts/verify-release.sh $(if $(TAG),$(TAG),latest) |
| 205 | 208 | ||
| 206 | .PHONY: linux windows clean appimage \ | 209 | .PHONY: linux windows clean appimage \ |
| 207 | tests fuzz-net-message-smoke checksums sign-checksums verify-release \ | 210 | tests fuzz-net-message-smoke fuzz-script-smoke checksums sign-checksums verify-release \ |
| 208 | deps-linux deps-windows deps-apt deps-secp256k1 deps-randomx | 211 | deps-linux deps-windows deps-apt deps-secp256k1 deps-randomx |
diff --git a/src/Makefile b/src/Makefile index ac0226e..cc52fd6 100644 --- a/src/Makefile +++ b/src/Makefile | |||
| @@ -121,8 +121,9 @@ NODE_LIBS = -L$(SECP_LIB) -lsecp256k1 \ | |||
| 121 | NODE_ONLY_OBJS = $(patsubst obj/%,obj-node/%,$(NODE_OBJS)) \ | 121 | NODE_ONLY_OBJS = $(patsubst obj/%,obj-node/%,$(NODE_OBJS)) \ |
| 122 | obj-node/main_gui.o obj-node/headless.o | 122 | obj-node/main_gui.o obj-node/headless.o |
| 123 | 123 | ||
| 124 | FUZZ_OBJS = $(patsubst obj/%,obj-fuzz/%,$(NODE_OBJS)) \ | 124 | FUZZ_BASE_OBJS = $(patsubst obj/%,obj-fuzz/%,$(NODE_OBJS)) |
| 125 | obj-fuzz/fuzz_net_message.o | 125 | FUZZ_NET_MESSAGE_OBJS = $(FUZZ_BASE_OBJS) obj-fuzz/fuzz_net_message.o |
| 126 | FUZZ_SCRIPT_OBJS = $(FUZZ_BASE_OBJS) obj-fuzz/fuzz_script.o | ||
| 126 | 127 | ||
| 127 | obj-node/%.o: %.cpp $(HEADERS) | 128 | obj-node/%.o: %.cpp $(HEADERS) |
| 128 | @mkdir -p obj-node | 129 | @mkdir -p obj-node |
| @@ -148,19 +149,35 @@ obj-fuzz/fuzz_net_message.o: fuzz_net_message.cpp $(HEADERS) | |||
| 148 | @mkdir -p obj-fuzz | 149 | @mkdir -p obj-fuzz |
| 149 | $(FUZZ_CXX) -c $(FUZZ_CXXFLAGS) -DBITFLASH_FUZZ -o $@ $< | 150 | $(FUZZ_CXX) -c $(FUZZ_CXXFLAGS) -DBITFLASH_FUZZ -o $@ $< |
| 150 | 151 | ||
| 151 | fuzz-net-message: $(FUZZ_OBJS) | 152 | obj-fuzz/fuzz_script.o: fuzz_script.cpp $(HEADERS) |
| 153 | @mkdir -p obj-fuzz | ||
| 154 | $(FUZZ_CXX) -c $(FUZZ_CXXFLAGS) -DBITFLASH_FUZZ -o $@ $< | ||
| 155 | |||
| 156 | fuzz-net-message: $(FUZZ_NET_MESSAGE_OBJS) | ||
| 152 | $(FUZZ_CXX) $(filter-out -fsanitize=fuzzer-no-link%,$(FUZZ_CXXFLAGS)) \ | 157 | $(FUZZ_CXX) $(filter-out -fsanitize=fuzzer-no-link%,$(FUZZ_CXXFLAGS)) \ |
| 153 | $(FUZZ_LDFLAGS) -o $@ $^ $(NODE_LIBS) | 158 | $(FUZZ_LDFLAGS) -o $@ $^ $(NODE_LIBS) |
| 154 | @echo "Built: fuzz-net-message" | 159 | @echo "Built: fuzz-net-message" |
| 155 | 160 | ||
| 161 | fuzz-script: $(FUZZ_SCRIPT_OBJS) | ||
| 162 | $(FUZZ_CXX) $(filter-out -fsanitize=fuzzer-no-link%,$(FUZZ_CXXFLAGS)) \ | ||
| 163 | $(FUZZ_LDFLAGS) -o $@ $^ $(NODE_LIBS) | ||
| 164 | @echo "Built: fuzz-script" | ||
| 165 | |||
| 156 | fuzz-net-message-smoke: fuzz-net-message | 166 | fuzz-net-message-smoke: fuzz-net-message |
| 157 | @mkdir -p ../work/fuzz-corpus/net-message | 167 | @mkdir -p ../work/fuzz-corpus/net-message |
| 158 | @printf '\277\040\134\375ping\000\000\000\000\000\000\000\000\000\000\000\000' > ../work/fuzz-corpus/net-message/ping-header | 168 | @printf '\277\040\134\375ping\000\000\000\000\000\000\000\000\000\000\000\000' > ../work/fuzz-corpus/net-message/ping-header |
| 159 | @printf '\277\040\134\375version\000\000\000\000\000\000\000\000\000' > ../work/fuzz-corpus/net-message/version-header | 169 | @printf '\277\040\134\375version\000\000\000\000\000\000\000\000\000' > ../work/fuzz-corpus/net-message/version-header |
| 160 | ./fuzz-net-message -runs=128 ../work/fuzz-corpus/net-message | 170 | ./fuzz-net-message -runs=128 ../work/fuzz-corpus/net-message |
| 161 | 171 | ||
| 172 | fuzz-script-smoke: fuzz-script | ||
| 173 | @mkdir -p ../work/fuzz-corpus/script | ||
| 174 | @printf '\000\121' > ../work/fuzz-corpus/script/op-true | ||
| 175 | @printf '\001\166\251\024\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\210\254' > ../work/fuzz-corpus/script/p2pkh-shape | ||
| 176 | @printf '\000\115\377\377' > ../work/fuzz-corpus/script/truncated-pushdata2 | ||
| 177 | ./fuzz-script -runs=128 ../work/fuzz-corpus/script | ||
| 178 | |||
| 162 | clean: | 179 | clean: |
| 163 | rm -rf obj obj-node obj-fuzz bitflash bitflash-node fuzz-net-message test_bip32 | 180 | rm -rf obj obj-node obj-fuzz bitflash bitflash-node fuzz-net-message fuzz-script test_bip32 |
| 164 | 181 | ||
| 165 | test_bip32: test_bip32.cpp bip32.cpp bip32.h bip39_english.h | 182 | test_bip32: test_bip32.cpp bip32.cpp bip32.h bip39_english.h |
| 166 | $(CXX) $(CXXFLAGS) -o $@ test_bip32.cpp bip32.cpp -lssl -lcrypto | 183 | $(CXX) $(CXXFLAGS) -o $@ test_bip32.cpp bip32.cpp -lssl -lcrypto |
| @@ -175,4 +192,4 @@ tests: test_bip32 bitflash-node | |||
| 175 | ./bitflash-node -selftest=net-message | 192 | ./bitflash-node -selftest=net-message |
| 176 | ./bitflash-node -selftest=consensus-limits | 193 | ./bitflash-node -selftest=consensus-limits |
| 177 | 194 | ||
| 178 | .PHONY: tests fuzz-net-message-smoke | 195 | .PHONY: tests fuzz-net-message-smoke fuzz-script-smoke |
diff --git a/src/fuzz_script.cpp b/src/fuzz_script.cpp new file mode 100644 index 0000000..28c84f6 --- /dev/null +++ b/src/fuzz_script.cpp | |||
| @@ -0,0 +1,59 @@ | |||
| 1 | // Copyright (c) 2026 Bitflash developers | ||
| 2 | // Distributed under the MIT/X11 software license. | ||
| 3 | // | ||
| 4 | // libFuzzer harness for raw script parsing and evaluation. It keeps the | ||
| 5 | // target deliberately small: arbitrary bytes become one CScript, then the | ||
| 6 | // parser, standard-script extractors and EvalScript all see that same input. | ||
| 7 | |||
| 8 | #include "headers_core.h" | ||
| 9 | |||
| 10 | #include <stdint.h> | ||
| 11 | #include <stddef.h> | ||
| 12 | |||
| 13 | // Stand-ins normally supplied by the GUI/headless entry objects. | ||
| 14 | map<string,string> mapAddressBook; | ||
| 15 | bool gPoolServerRunning = false; | ||
| 16 | void MainFrameRepaint() {} | ||
| 17 | |||
| 18 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) | ||
| 19 | { | ||
| 20 | if (size < 1 || size > 4096) | ||
| 21 | return 0; | ||
| 22 | |||
| 23 | try | ||
| 24 | { | ||
| 25 | int nHashType = data[0]; | ||
| 26 | const unsigned char* pbegin = data + 1; | ||
| 27 | const unsigned char* pend = data + size; | ||
| 28 | CScript script(pbegin, pend); | ||
| 29 | |||
| 30 | CScript::const_iterator pc = script.begin(); | ||
| 31 | opcodetype opcode; | ||
| 32 | vector<unsigned char> vchPush; | ||
| 33 | while (pc < script.end()) | ||
| 34 | { | ||
| 35 | if (!script.GetOp(pc, opcode, vchPush)) | ||
| 36 | break; | ||
| 37 | } | ||
| 38 | |||
| 39 | script.GetSigOpCount(); | ||
| 40 | script.ToString(); | ||
| 41 | |||
| 42 | vector<unsigned char> vchPubKey; | ||
| 43 | ExtractPubKey(script, false, vchPubKey); | ||
| 44 | uint160 hash160; | ||
| 45 | ExtractHash160(script, hash160); | ||
| 46 | |||
| 47 | CTransaction tx; | ||
| 48 | tx.vin.push_back(CTxIn(COutPoint(), CScript())); | ||
| 49 | tx.vout.push_back(CTxOut(0, CScript())); | ||
| 50 | |||
| 51 | vector<vector<unsigned char> > stack; | ||
| 52 | EvalScript(script, tx, 0, nHashType, &stack); | ||
| 53 | } | ||
| 54 | catch (...) | ||
| 55 | { | ||
| 56 | } | ||
| 57 | |||
| 58 | return 0; | ||
| 59 | } | ||