{"id":31921816,"url":"https://github.com/henryhchchc/lsp-fuzz","last_synced_at":"2025-10-13T22:54:34.353Z","repository":{"id":317235318,"uuid":"868721870","full_name":"henryhchchc/lsp-fuzz","owner":"henryhchchc","description":"A grey-box hybrid fuzzer that generates test cases for language servers","archived":false,"fork":false,"pushed_at":"2025-10-07T05:58:39.000Z","size":3438,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-07T07:25:50.020Z","etag":null,"topics":["fuzzing","language-server-protocol","libafl","lsp"],"latest_commit_sha":null,"homepage":"https://aka.henryhc.net/lspfuzz","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/henryhchchc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["henryhchchc"]}},"created_at":"2024-10-07T04:14:39.000Z","updated_at":"2025-10-07T05:58:43.000Z","dependencies_parsed_at":"2025-09-29T18:39:49.655Z","dependency_job_id":null,"html_url":"https://github.com/henryhchchc/lsp-fuzz","commit_stats":null,"previous_names":["henryhchchc/lsp-fuzz"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/henryhchchc/lsp-fuzz","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/henryhchchc%2Flsp-fuzz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/henryhchchc%2Flsp-fuzz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/henryhchchc%2Flsp-fuzz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/henryhchchc%2Flsp-fuzz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/henryhchchc","download_url":"https://codeload.github.com/henryhchchc/lsp-fuzz/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/henryhchchc%2Flsp-fuzz/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017087,"owners_count":26085984,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-13T02:00:06.723Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fuzzing","language-server-protocol","libafl","lsp"],"created_at":"2025-10-13T22:54:31.933Z","updated_at":"2025-10-13T22:54:34.346Z","avatar_url":"https://github.com/henryhchchc.png","language":"Rust","funding_links":["https://github.com/sponsors/henryhchchc"],"categories":[],"sub_categories":[],"readme":"# LSPFuzz: Hunting Bugs in Language Servers\n\nLSPFuzz is a grey-box hybrid fuzzer that generates test cases for [Language Servers](https://microsoft.github.io/language-server-protocol/).\nIt is implemented based on [LibAFL](https://github.com/AFLplusplus/LibAFL).\n\n## Technical Details\n\nLSPFuzz is equipped with a two-stage mutation pipeline that produces valid yet diverse inputs to trigger various analysis routines in LSP servers.\nTo learn more about how it works, please check out the following research paper:\n\nHengcheng Zhu, Songqiang Chen, Valerio Terragni, Lili Wei, Jiarong Wu, Yepang Liu, and Shing-Chi Cheung.\n**LSPFuzz: Hunting Bugs in Language Servers.**\nIn _Proceedings of the 40\u003csup\u003eth\u003c/sup\u003e IEEE/ACM International Conference on Automated Software Engineering._ Seoul, South Korea. November 2025.\n\n[🎤Conference](https://conf.researchr.org/details/ase-2025/ase-2025-papers/203/LSPFuzz-Hunting-Bugs-in-Language-Servers)\n| [📄Preprint](https://scholar.henryhc.net/files/publications/2025/ASE2025-LSPFuzz.pdf)\n| [📦Artifacts](https://doi.org/10.5281/zenodo.17052142)\n\nIf you use LSPFuzz for academic purposes, please cite the above paper.\n\n## Usage\n\n### Preparation\n\n1. Prepare a fuzz target compatible with [AFL++](https://github.com/AFLplusplus/AFLplusplus).\n   It is highly recommended to use the [LTO mode](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.lto.md) and [persistent mode](https://github.com/AFLplusplus/AFLplusplus/blob/stable/instrumentation/README.persistent_mode.md).\n   The following is an annotated template for a fuzz target.\n\n   ```c++\n   #include \"your_header_file.h\"\n\n   #ifndef __AFL_FUZZ_TESTCASE_LEN\n    // The following definitions allow compilation without the AFL++ compiler.\n    ssize_t fuzz_len;\n    #define __AFL_FUZZ_TESTCASE_LEN fuzz_len\n    const uint8_t fuzz_buf[1024000];\n    #define __AFL_FUZZ_TESTCASE_BUF fuzz_buf\n    #define __AFL_FUZZ_INIT() void sync(void);\n    #define __AFL_LOOP(x) ((fuzz_len = read(0, fuzz_buf, sizeof(fuzz_buf))) \u003e 0 ? 1 : 0)\n    #define __AFL_INIT() sync()\n   #endif\n\n   __AFL_FUZZ_INIT();\n\n   int main(int argc, const char* argv[]) {\n\n    #ifdef __AFL_HAVE_MANUAL_CONTROL\n      __AFL_INIT();\n    #endif\n\n    // [Initialization]\n    // Perform some one-time initialization for the target LSP server.\n    // Or call `LLVMFuzzerInitialize(argc, argv)` here.\n\n    const uint8_t *buf = __AFL_FUZZ_TESTCASE_BUF;\n    while (__AFL_LOOP(10000)) {\n        ssize_t len = __AFL_FUZZ_TESTCASE_LEN;\n        // [Input Processing]\n        // Process an input here:\n        //   1. Read `len` bytes from `buf` for LSP inputs, as if they were read from `stdin`.\n        //   2. Process the LSP inputs. Note that the input contains the `Content-Length` headers.\n        //   3. Release resources and reset states\n        // Or call `LLVMFuzzerTestOneInput(buf, len)` here.\n    }\n    return 0;\n   }\n   ```\n\n\u003e [!NOTE]\n\u003e Although persistent mode can significantly improve the fuzzing efficiency, users need to make sure the resource are properly released and states are reset in the fuzzing loop.\n\n2. Obtaining the coverage map size\n\n```bash\nAFL_DUMP_MAP_SIZE=1 ./fuzz-target\n```\n\n3. Mine code fragments for code generation.\n\n```bash\nlsp-fuzz-cli mine-code-fragments \\\n  --search-directory \u003ccode-dir\u003e \\ # A directory containing code files of the target language of the LSP servers\n  --output \u003cfragment-output\u003e # The file to store the mined code fragments\n```\n\n### Start Fuzzing\n\n```bash\nlsp-fuzz-cli fuzz \\\n  --state \u003cstate-dir\u003e \\ # The directory to store the fuzzing state (e.g., generated inputs, found crashes)\n  --lsp-executable \u003cfuzz-target\u003e \\ # The executable file of the LSP server to fuzz target\n  --language-fragments Language=\u003cfragment-output\u003e\\ # Comma-separated list of files containing the mined code fragments, for example, `C=c.frag,CPlusPlus=cpp.frag`\n  --coverage-map-size \u003ccoverage-map-size\u003e \\ # The size of the coverage map to use for coverage-guided fuzzing\n  --time-budget 24 # The time budget for fuzzing in hours\n```\n\nTo lean more about the options, run `lsp-fuzz-cli fuzz --help`.\n\n### Reproduce Detected Crashes\n\n1. Export the generated crash inducing inputs\n\n```bash\nlsp-fuzz-cli export \\\n  --input \u003cstate-dir\u003e/solutions \\ # The directory containing the generated crash inducing inputs\n  --output \u003cexport-directory\u003e # The directory to store the exported crash inducing inputs\n```\n\nThe contents of `\u003cexport-directory\u003e` will be organized as follows:\n\n```\n\u003cexport-directory\u003e\n├── \u003cinput-id-1\u003e\n│   ├── workspace\n│   │   ├── file1.txt\n│   │   └── file2.txt\n│   └── requests\n│       ├── message_0001\n│       └── message_0002\n├── \u003cinput-id-2\u003e\n│   ├── workspace\n│   │   ├── file1.txt\n│   │   └── file2.txt\n│   └── requests\n│       ├── message_0001\n│       └── message_0002\n└── ...\n```\n\nEach directory `\u003cinput-id\u003e` represents a unique input generated by LSPFuzz.\nWithin each `\u003cinput-id\u003e` directory, there are two subdirectories: `workspace` and `requests`.\nThe `workspace` directory contains the code files, and the `requests` directory contains the LSP requests that were sent to the LSP server during the fuzzing process.\n\n\u003e [!NOTE]\n\u003e Do not move the exported test cases because the LSP requests are encoded with _absolute paths_, moving them will invalidate the requests.\n\n2. Feed the exported input to the LSP server\n\nTo reproduce the crash, `cd` to a directory containing the exported inputs.\n\n```bash\ncat requests/* | ./target-lsp-server\n```\n\nNote that `target-lsp-server` is the actual LSP server under test, not the fuzz target.\nMake sure it reads requests from `stdin`.\nTo reproduce bugs caught by sanitizers, `target-lsp-server` should be compiled with sanitizers enabled.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhenryhchchc%2Flsp-fuzz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhenryhchchc%2Flsp-fuzz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhenryhchchc%2Flsp-fuzz/lists"}