Merge #1004: Fuzzing integration

3017b88e27849830530fc0ccf77df215889d17ba fuzz: fuzzing integrations, fuzz the descriptors module (Antoine Poinsot)
f7924fb9dcd4d91d404d1da736021f83619e2529 descriptors: lifting *can* fail (Antoine Poinsot)

Pull request description:

  This introduces fuzzing into our project with two fuzz targets exercising our descriptor parsing logic. See the commit messages for details. This found a crash (first commit).

  This was motivated by testing the work on Taproot (#985).

ACKs for top commit:
  darosior:
    ACK 3017b88e27849830530fc0ccf77df215889d17ba - it's not interfering with anything in the repo, been running these for half a day with no crash.

Tree-SHA512: 25c1b64a86585fc5f676c3526e2dae945b74c6b0cb4ce2d9db33dc48aa85aaa11a07b279838703d62c9ca00cf39cc34577ca19c0a8f9aaf5327266eb7be6dce0
This commit is contained in:
Antoine Poinsot 2024-03-13 14:28:35 +01:00
commit f17092375e
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
8 changed files with 1060 additions and 1 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ gui/ui/Cargo.lock
gui/ui/examples/design-system/Cargo.lock gui/ui/examples/design-system/Cargo.lock
Xcode_12.2.xip Xcode_12.2.xip
.idea/ .idea/
fuzz/corpus

747
fuzz/Cargo.lock generated Normal file
View File

@ -0,0 +1,747 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "allocator-api2"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110"
dependencies = [
"derive_arbitrary",
]
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "bdk_coin_select"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0320167c3655e83f0415d52f39618902e449186ffc7dfb090f922f79675c316"
[[package]]
name = "bech32"
version = "0.10.0-beta"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98f7eed2b2781a6f0b5c903471d48e15f56fb4e1165df8a9a2337fd1a59d45ea"
[[package]]
name = "bip39"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93f2635620bf0b9d4576eb7bb9a38a55df78bd1205d26fa994b25911a69f212f"
dependencies = [
"bitcoin_hashes 0.11.0",
"serde",
"unicode-normalization",
]
[[package]]
name = "bitcoin"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd00f3c09b5f21fb357abe32d29946eb8bb7a0862bae62c0b5e4a692acbbe73c"
dependencies = [
"base64 0.21.7",
"bech32",
"bitcoin-internals",
"bitcoin_hashes 0.13.0",
"hex-conservative",
"hex_lit",
"secp256k1",
"serde",
]
[[package]]
name = "bitcoin-internals"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9425c3bf7089c983facbae04de54513cce73b41c7f9ff8c845b54e7bc64ebbfb"
dependencies = [
"serde",
]
[[package]]
name = "bitcoin_hashes"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90064b8dee6815a6470d60bad07bbbaee885c0e12d04177138fa3291a01b7bc4"
[[package]]
name = "bitcoin_hashes"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1930a4dabfebb8d7d9992db18ebe3ae2876f0a305fab206fd168df931ede293b"
dependencies = [
"bitcoin-internals",
"hex-conservative",
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "cc"
version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
dependencies = [
"jobserver",
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "derive_arbitrary"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "dirs"
version = "5.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fern"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9f0c14694cbd524c8720dd69b0e3179344f04ebb5f90f2e4a440c6ea3b2f1ee"
dependencies = [
"log",
]
[[package]]
name = "getrandom"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashlink"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [
"hashbrown",
]
[[package]]
name = "hex-conservative"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30ed443af458ccb6d81c1e7e661545f94d3176752fb1df2f543b902a1e0f51e2"
[[package]]
name = "hex_lit"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3011d1213f159867b13cfd6ac92d2cd5f1345762c63be3554e84092d85a50bbd"
[[package]]
name = "itoa"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
[[package]]
name = "jobserver"
version = "0.1.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab46a6e9526ddef3ae7f787c06f0f2600639ba80ea3eade3d8e670a2230f51d6"
dependencies = [
"libc",
]
[[package]]
name = "jsonrpc"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26d9104d516092f092d97448787505881fdb6518293b2d6500bf9c180c839dd"
dependencies = [
"base64 0.13.1",
"minreq",
"serde",
"serde_json",
]
[[package]]
name = "liana"
version = "4.0.0"
dependencies = [
"backtrace",
"bdk_coin_select",
"bip39",
"dirs",
"fern",
"getrandom",
"jsonrpc",
"libc",
"log",
"miniscript",
"rdrand",
"rusqlite",
"serde",
"serde_json",
"toml",
]
[[package]]
name = "liana-fuzz"
version = "0.0.0"
dependencies = [
"arbitrary",
"liana",
"libfuzzer-sys",
"secp256k1",
]
[[package]]
name = "libc"
version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libfuzzer-sys"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a96cfd5557eb82f2b83fed4955246c988d331975a002961b07c81584d107e7f7"
dependencies = [
"arbitrary",
"cc",
"once_cell",
]
[[package]]
name = "libredox"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8"
dependencies = [
"bitflags 2.4.2",
"libc",
"redox_syscall",
]
[[package]]
name = "libsqlite3-sys"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "log"
version = "0.4.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "miniscript"
version = "11.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86a23dd3ad145a980e231185d114399f25a0a307d2cd918010ddda6334323df9"
dependencies = [
"bech32",
"bitcoin",
"bitcoin-internals",
"serde",
]
[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
[[package]]
name = "minreq"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb3371dfc7b772c540da1380123674a8e20583aca99907087d990ca58cf44203"
dependencies = [
"log",
"serde",
"serde_json",
]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "pkg-config"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec"
[[package]]
name = "proc-macro2"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rdrand"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d92195228612ac8eed47adbc2ed0f04e513a4ccb98175b6f2bd04d963b533655"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa"
dependencies = [
"bitflags 1.3.2",
]
[[package]]
name = "redox_users"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4"
dependencies = [
"getrandom",
"libredox",
"thiserror",
]
[[package]]
name = "rusqlite"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
dependencies = [
"bitflags 2.4.2",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "ryu"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
[[package]]
name = "secp256k1"
version = "0.28.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24b59d129cdadea20aea4fb2352fa053712e5d713eee47d700cd4b2bc002f10"
dependencies = [
"bitcoin_hashes 0.13.0",
"secp256k1-sys",
"serde",
]
[[package]]
name = "secp256k1-sys"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5d1746aae42c19d583c3c1a8c646bfad910498e2051c551a7f2e3c0c9fbb7eb"
dependencies = [
"cc",
]
[[package]]
name = "serde"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.197"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
version = "1.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "syn"
version = "2.0.52"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "tinyvec"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
dependencies = [
"tinyvec_macros",
]
[[package]]
name = "tinyvec_macros"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "toml"
version = "0.5.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234"
dependencies = [
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-normalization"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921"
dependencies = [
"tinyvec",
]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]

31
fuzz/Cargo.toml Normal file
View File

@ -0,0 +1,31 @@
[package]
name = "liana-fuzz"
version = "0.0.0"
publish = false
edition = "2018"
[package.metadata]
cargo-fuzz = true
[dependencies]
libfuzzer-sys = "0.4"
arbitrary = { version = "1", features = ["derive"] }
secp256k1 = { version = "0.28", features = ["global-context-less-secure"] }
[dependencies.liana]
path = ".."
[[bin]]
name = "descriptor_parse"
path = "fuzz_targets/descriptor_parse.rs"
test = false
doc = false
bench = false
[[bin]]
name = "descriptors"
path = "fuzz_targets/descriptors.rs"
test = false
doc = false
bench = false

5
fuzz/README.md Normal file
View File

@ -0,0 +1,5 @@
# Liana fuzzing targets
This folder hosts the fuzz targets for Liana. We simply use
[`cargo-fuzz`](https://github.com/rust-fuzz/cargo-fuzz) for now (see there for instructions on how
to run the targets).

View File

@ -0,0 +1,76 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use liana::{descriptors::LianaDescriptor, miniscript::bitcoin::Network};
use secp256k1::global::SECP256K1;
use std::str::{self, FromStr};
// Hacky way to detect too deeply nested wrappers, which would make rust-miniscript stack overflow
// when parsing them.
fn too_deep_wrappers(desc_str: &str) -> bool {
let mut wrapper_count = 0;
for c in desc_str.chars() {
if c == ':' || c == '(' || c == ')' || c == ',' {
if c == ':' && wrapper_count > 10 {
return true;
}
wrapper_count = 0;
} else {
wrapper_count += 1;
}
}
false
}
fuzz_target!(|data: &[u8]| {
let desc_str = match str::from_utf8(data) {
Ok(s) => s,
Err(_) => return,
};
if data.len() > 10_000 {
return;
}
// Rust-miniscript uses a recursive parsing algorithm which could make us crash when trying to
// parse nested fragments. Until they fix it, rule out the most trivial recursion overflow the
// fuzzer can generate: too deep wrappers.
// FIXME: this shouldn't be necessary when upgrading to the next rust-miniscript version.
if too_deep_wrappers(&desc_str) {
return;
}
let desc = match LianaDescriptor::from_str(desc_str) {
Ok(d) => d,
Err(_) => return,
};
// The descriptor must roundtrip.
assert_eq!(desc, LianaDescriptor::from_str(&desc.to_string()).unwrap());
// We can get the policy out of this desc and a desc out of this policy, but it's not
// guaranteed to roundtrip: policy->descriptor involves a compilation.
LianaDescriptor::new(desc.policy());
// Exercise the various methods on the descriptor. None should crash.
desc.receive_descriptor();
desc.change_descriptor();
desc.first_timelock_value();
desc.max_sat_weight();
desc.max_sat_vbytes();
desc.spender_input_size();
// Exercise the various methods of derived descriptors. None should crash.
let der_index = 42.into();
let der_descs = [
desc.receive_descriptor().derive(der_index, &SECP256K1),
desc.change_descriptor().derive(der_index, &SECP256K1),
];
for desc in der_descs {
desc.address(Network::Bitcoin);
desc.script_pubkey();
desc.witness_script();
desc.bip32_derivations();
}
});

View File

@ -0,0 +1,192 @@
#![no_main]
use arbitrary::{Arbitrary, Unstructured};
use libfuzzer_sys::fuzz_target;
use liana::{
descriptors::{LianaDescriptor, LianaPolicy, PathInfo},
miniscript::{
bitcoin::{bip32, Network, Psbt},
descriptor,
},
};
use secp256k1::global::SECP256K1;
use std::{collections::BTreeMap, str::FromStr};
#[derive(Arbitrary, Debug)]
struct PathConfig {
pub thresh: u8,
pub count: u8,
}
impl PathConfig {
/// Generate the data for this path. We reuse the same xpub across the board as it doesn't
/// matter. However we change the fingerprint, as it matters for the spend info analysis.
pub fn info(&self, path_index: u8) -> Option<PathInfo> {
if self.thresh > self.count || self.count > 100 {
// Not worth wasting time generating pathological or huge policies.
return None;
}
let public_key = secp256k1::PublicKey::from_str(
"0250929b74c1a04954b78b4b6035e97a5e078a5a0f28ec96d547bfee9ace803ac0",
)
.expect("Valid pubkey: NUMS from BIP341");
let dummy_fg = [0, 0, path_index, 0].into();
let xpub = bip32::Xpub {
network: Network::Bitcoin,
depth: 0,
parent_fingerprint: dummy_fg,
child_number: 0.into(),
public_key,
chain_code: [path_index; 32].into(),
};
if self.count == 1 {
Some(PathInfo::Single(
descriptor::DescriptorPublicKey::MultiXPub(descriptor::DescriptorMultiXKey {
origin: Some((dummy_fg, vec![0.into()].into())),
xkey: xpub,
derivation_paths: descriptor::DerivPaths::new(vec![
vec![0.into()].into(),
vec![1.into()].into(),
])
.unwrap(),
wildcard: descriptor::Wildcard::Unhardened,
}),
))
} else {
let keys: Vec<_> = (0..self.count)
.map(|i| {
descriptor::DescriptorPublicKey::MultiXPub(descriptor::DescriptorMultiXKey {
origin: Some(([0, 0, path_index, i].into(), vec![0.into()].into())),
xkey: xpub,
derivation_paths: descriptor::DerivPaths::new(vec![
vec![(i as u32).into()].into(),
vec![(i as u32 + 1).into()].into(),
])
.unwrap(),
wildcard: descriptor::Wildcard::Unhardened,
})
})
.collect();
Some(PathInfo::Multi(self.thresh.into(), keys))
}
}
}
#[derive(Arbitrary, Debug)]
struct RecPathConfig {
pub timelock: u8,
pub path: PathConfig,
}
#[derive(Debug)]
struct DummyPsbt(pub Option<Psbt>);
impl<'a> Arbitrary<'a> for DummyPsbt {
fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result<Self> {
let psbt_bytes = <&[u8]>::arbitrary(u)?;
Ok(Self(Psbt::deserialize(&psbt_bytes).ok()))
}
}
// TODO: be smarter instead of parsing a PSBT, have a config which generates an interesting PSBT.
/// The configuration for this target: the descriptor policy to be used, and various things we use
/// in exercising the various methods of the descriptor.
#[derive(Arbitrary, Debug)]
struct Config {
pub prim_path: PathConfig,
pub recovery_paths: Vec<RecPathConfig>,
pub der_index: u16,
pub dummy_psbt: DummyPsbt,
}
fuzz_target!(|config: Config| {
// Not worth wasting time compiling huge policies.
if config.recovery_paths.len() > 100 {
return;
}
// Generate the descriptor according to the policy in the config.
let prim_path_info = if let Some(info) = config.prim_path.info(0) {
info
} else {
return;
};
let rec_paths_info: Option<BTreeMap<_, _>> = config
.recovery_paths
.into_iter()
.enumerate()
.map(|(idx, rec_conf)| Some((rec_conf.timelock.into(), rec_conf.path.info(idx as u8 + 1)?)))
.collect();
let rec_paths_info = if let Some(info) = rec_paths_info {
info
} else {
return;
};
let policy = if let Ok(policy) = LianaPolicy::new(prim_path_info, rec_paths_info) {
policy
} else {
return;
};
let desc = LianaDescriptor::new(policy);
// The descriptor must roundtrip.
assert_eq!(desc, LianaDescriptor::from_str(&desc.to_string()).unwrap());
// We can get the policy out of this desc and a desc out of this policy, but it's not
// guaranteed to roundtrip: policy->descriptor involves a compilation.
LianaDescriptor::new(desc.policy());
// Exercise the various methods on the descriptor. None should crash.
desc.receive_descriptor();
desc.change_descriptor();
desc.first_timelock_value();
desc.max_sat_weight();
desc.max_sat_vbytes();
desc.spender_input_size();
// Exercise the various methods of derived descriptors. None should crash.
let der_index = (config.der_index as u32).into();
let der_descs = [
desc.receive_descriptor().derive(der_index, &SECP256K1),
desc.change_descriptor().derive(der_index, &SECP256K1),
];
for desc in &der_descs {
desc.address(Network::Bitcoin);
desc.script_pubkey();
desc.witness_script();
desc.bip32_derivations();
}
// Exercise the methods gathering information from a PSBT. TODO: get more useful PSBTs.
if let Some(mut psbt) = config.dummy_psbt.0 {
desc.change_indexes(&psbt, &SECP256K1);
// Get the spend info without populating the PSBT at all.
let _ = desc.partial_spend_info(&psbt);
// Populate the PSBT. We arbitrarily use the receive desc for inputs and the change desc
// for outputs.
let rec_desc = &der_descs[0];
for psbt_in in psbt.inputs.iter_mut() {
psbt_in.witness_script = Some(rec_desc.witness_script());
psbt_in.bip32_derivation = rec_desc.bip32_derivations();
}
let change_desc = &der_descs[1];
for psbt_out in psbt.outputs.iter_mut() {
psbt_out.bip32_derivation = change_desc.bip32_derivations();
}
// Now get the spend info again with these info.
let _ = desc.partial_spend_info(&psbt);
// Prune all the info but those for the latest available path, and get the spend info
// again.
if let Ok(psbt) = desc.prune_bip32_derivs_last_avail(psbt) {
let _ = desc.partial_spend_info(&psbt);
}
}
});

View File

@ -25,6 +25,7 @@ pub enum LianaPolicyError {
/// The spending policy is not a valid Miniscript policy: it may for instance be malleable, or /// The spending policy is not a valid Miniscript policy: it may for instance be malleable, or
/// overflow some limit. /// overflow some limit.
InvalidPolicy(compiler::CompilerError), InvalidPolicy(compiler::CompilerError),
PolicyAnalysis(miniscript::Error),
} }
impl std::fmt::Display for LianaPolicyError { impl std::fmt::Display for LianaPolicyError {
@ -54,6 +55,7 @@ impl std::fmt::Display for LianaPolicyError {
"Descriptor is not compatible with a Liana spending policy." "Descriptor is not compatible with a Liana spending policy."
), ),
Self::InvalidPolicy(e) => write!(f, "Invalid Miniscript policy: {}", e), Self::InvalidPolicy(e) => write!(f, "Invalid Miniscript policy: {}", e),
Self::PolicyAnalysis(e) => write!(f, "Analyzing the policy of the miniscript: {}", e),
} }
} }
} }
@ -452,7 +454,7 @@ impl LianaPolicy {
}; };
let policy = ms let policy = ms
.lift() .lift()
.expect("Lifting can't fail on a Miniscript") .map_err(LianaPolicyError::PolicyAnalysis)?
.normalized(); .normalized();
// The policy must always be "1 of N spending paths" with at least an always-available // The policy must always be "1 of N spending paths" with at least an always-available

View File

@ -1405,5 +1405,10 @@ mod tests {
)); ));
} }
#[test]
fn unliftable_miniscript() {
LianaDescriptor::from_str("wsh(0)").unwrap_err();
}
// TODO: test error conditions of deserialization. // TODO: test error conditions of deserialization.
} }