
Spezialisiertes Reasoning-LLM zur Erkennung von Schwachstellen im Quellcode in C/C++ und Python, mit Datensatzerstellung, SFT/DPO-Training und Skripten zur Reproduktion der Ergebnisse.
git lfs install # one-time setup
git clone https://github.com/ucsb-mlsec/VulnLLM-R.git
cd VulnLLM-R
# If you cloned before installing Git LFS, run: git lfs pull
conda create -n vulnscan python=3.11
conda activate vulnscan
pip install -e . -e ./vulscan/train/LLaMA-Factory -e ./vulscan/model_zoo
# generate VulnLLM-R-7B's results
python -m vulscan.test.test --output_dir results/test_data --dataset_path ./datasets/test/function_level/ ./datasets/test/repo_level/ --language python c java --model UCSB-SURFI/VulnLLM-R-7B --requests_per_minute 1000 --save --use_cot --batch_size 4 --tp 2 --vllm --max_tokens 8192 --random_cwe
python -m vulscan.test.test_hf \
--output_dir results/test_hf \
--hf_dataset UCSB-SURFI/VulnLLM-R-Test-Data \
--hf_split repo_level function_level \
--language c python java \
--model UCSB-SURFI/VulnLLM-R-7B \
--save --use_cot --vllm --tp 2
# [optional] generate other models' results with our shell script
# remember to add your API keys to .env file if you want to run commercial models
# use ./run_test.sh -h for more options
./vulscan/test/run_test.sh -o results/test_data -t 2 # -o means output directory, -t means tensor parallelism
./vulscan/test/run_test.sh -o results/test_data -M o3-mini # -M means model name, which runs only one model.
./vulscan/test/run_test.sh -o results/test_data -M gpt-5.4 -e high # -e sets reasoning effort (e.g., none/low/medium/high/xhigh)
./vulscan/test/run_test.sh -o results/test_data -M claude-opus-4-6 -e high
# [optional] draw plot to compare with other models
python plots/plot_language_comparison_models.py --results-dir results/test_data
python plots/plot_model_size_scatter.py --results-dir results/test_data # Note: Labels may overlap with scatter points. Adjust text positions manually if needed.
Wir stellen außerdem die reduzierte Reasoning-Version der destillierten Datensätze bereit:
Führen Sie die vorhandenen Datensätze zur Erkennung von Schwachstellen auf Funktionsebene zusammen: PrimeVul [1], SecCodePLT [2], Juliet [3], Sven [4] und Arvo [5]. Von diesen Datensätzen enthält PrimeVul die komplexesten Funktionen. Wir erstellen zwei Trainingssätze: clean (ohne PrimeVul) und noisy (mit PrimeVul), sodass wir mit relativ einfachen Datensätzen trainieren und auf dem komplexen PrimeVul-Datensatz testen können. Beachten Sie, dass die Benennung des Trainingssatzes mit PrimeVul als noisy nicht bedeutet, dass der Datensatz verrauscht ist. Es ist ein relativ willkürlicher Name, den wir am Anfang verwendet haben.
vulscan/data_process/data_utils eine Reihe von Skripten
zum Verarbeiten und Zusammenführen der Datensätze.
raw_to_us.py: Führt die Rohdaten in unseren Datensatz zusammen und entfernt redundante Datencheck_cwe_correct.py: Berechnet die Genauigkeit für jede CWE-Kategoriegenerate_arvo_raw_data.py: Erzeugt strukturierte Rohdaten aus dem Arvo-Datensatzarvo_to_us.py: Formatiert strukturierte Arvo-Rohdaten in unser Datensatzformat umsplit_good_bad_for_juliet.py: Extrahiert Daten aus dem rohen Juliet-1.3-Datensatz und konvertiert sie in das
erforderliche Format, das Teil unseres C-Clean-Datensatzes istadd_sven_to_clean_dataset.py: Extrahiert Daten aus dem Sven-Datensatz, die Teil unseres C-Clean-Datensatzes sindsync_large_small.py: Synchronisiert die Änderungen von noisy_dataset/large_train/c nach
noisy_dataset/small_train/cremove_testing_from_training.py: Fügt jedem Datenelement das human-Tag hinzu, was bedeutet, dass der Punkt von
einem Menschen verifiziert und als Testdaten verwendet wurde
-: Fügt das Feld related_cwe zum Datensatz hinzu.Nach der Erstellung der Datensätze generieren wir Reasoning-Daten für unseren Trainingssatz.
Wir fragen die Reasoning-Modelle DeepSeek-r1 und QwQ ab, um die Reasoning-Daten zu erzeugen, und filtern diejenigen mit
sehr langen Reasoning-Ketten heraus.
Der Code zum Generieren der Reasoning-Daten befindet sich in vulscan/data_process/generate_reasoning, und die
Reasoning-Daten werden in datasets/reasoning_data gespeichert.
cd vulscan/data_process/generate_reasoning
generate_reasoning/generate.py ist das Hauptskript zum Generieren der Reasoning-Daten.
Für jeden Datenpunkt werden n Reasoning-Datenproben erzeugt und diejenige mit der richtigen Antwort und der kürzesten
Länge ausgewählt.
Beispiele für die Ausführung mit den Modellen QwQ und DeepSeek-r1 (unter Verwendung der together.AI-API, die langsamer,
aber stabiler ist als die offizielle API) sind unten aufgeführt:
python generate.py \
--tp 2 \
--dataset_type clean_dataset \
--batch_size 200 \
--n 8 \
--training_set train \
--model_name Qwen/QwQ-32B
# or
python generate.py \
--dataset_type noisy_dataset \
--batch_size 200 \
--n 8 \
--training_set small_train \
--model_name together-deepseek-reasoner \
--together_deepseek
Nach der Generierung der rohen Reasoning-Daten können wir ein weiteres Modell verwenden, um sie zusammenzufassen und zu verkürzen, ohne die Struktur zu zerstören
python extract_reasoning.py
Wir können die Reasoning-Daten mit generate_reasoning/filter.py weiter anhand bestimmter Längen filtern;
num_processes wird entsprechend Ihrer Anzahl an CPU-Kernen angepasst.
python filter.py \
--dataset_type noisy_dataset \
--training_set small_train \
--model_name Qwen/QwQ-32B \
--filter_input_length 16000 \
--filter_all_length 32000 \
--num_processes 16 \
--filter_correct_only # for filtering wrong predictions
# --model_name together-deepseek-reasoner \
Schließlich müssen wir die generierten Reasoning-Daten für das Zielmodell, das wir trainieren möchten (Qwen-Instruct), neu formatieren.
python reformat_ds.py \
--dataset_type noisy_dataset \
--training_set small_train \
--model_name Qwen/QwQ-32B \
--filter_input_length 16000 \
--filter_all_length 32000 \
--push_to_hub \
--push_to_hub_organization secmlr \
--filter_correct_only
Für den DPO-Datensatz:
python generate_dpo.py \
--tp 2 --dataset_type clean_dataset \
--batch_size 200 --n 8 --training_set train \
--model secmlr/VD-QWQ-Clean-8k_qwen2_7B_full_sft_1e-5
Weitere Details finden Sie in vulscan/train/README.md
Die Ergebnisse werden im Verzeichnis results/test_qwen/results.json gespeichert.
Wenn Sie unsere Ergebnisse reproduzieren möchten, können Sie den folgenden Befehl ausführen:
# open-source model
python -m vulscan.test.test --output_dir results/one_of_4 \
--dataset_path ./datasets/test/test_clean ./datasets/test/test_primevul_pair \
--language c python --model Qwen/Qwen2.5-7B-Instruct \
--requests_per_minute 100 --save --use_cot \
--use_policy --batch_size 4 --tp 2 --vllm --max_tokens 16384 \
--random_cwe
# api model
python -m vulscan.test.test --output_dir results/one_of_4 \
--dataset_path ./datasets/test/test_clean ./datasets/test/test_primevul_pair \
--language c python --model o3-mini-2025-01-14 \
--requests_per_minute 100 --save --use_cot \
--use_policy --batch_size 4 --max_tokens 16384 --random_cwe
# local saved model
python -m vulscan.test.test --output_dir results/one_of_4 \
--dataset_path ./datasets/test/test_clean ./datasets/test/test_primevul_pair \
--language c python \
--model vulscan/train/result/VD-QWQ-Clean-16k/qwen2_7B_full_sft_1e-5 \
--requests_per_minute 100 --save --use_cot --use_policy \
--batch_size 4 --tp 2 --vllm --max_tokens 16384 --random_cwe
# our model
python -m vulscan.test.test --output_dir results/one_of_4 \
--dataset_path ./datasets/test/test_clean ./datasets/test/test_primevul_pair \
--language c python \
--model secmlr/VD-QWQ-Noisy-Small-8k_qwen2_7B_full_sft_1e-5 --revision aa3235b \
--requests_per_minute 100 --save --use_cot --use_policy \
--batch_size 4 --tp 2 --vllm --max_tokens 16384 \
--random_cwe # whether to randomize the order of cwe and related cwes
Nach dem Test werden die Modellantworten und die Leistung im Verzeichnis results/test_data gespeichert.
Wenn Sie die Leistung anhand der Modellantworten berechnen möchten, können Sie den folgenden Befehl ausführen:
python -m vulscan.test.test_existing_json \
--json_file results/test_data/datasets_test_test_clean__cot_c_policy_QwQ-32B-Preview.json # the results file
python generate_constitution.py --model gpt-4o --input_dir results/train --output_dir results/train/constitution
@article{nie2025vulnllmrspecializedreasoningllm,
title={VulnLLM-R: Specialized Reasoning LLM with Agent Scaffold for Vulnerability Detection},
author={Yuzhou Nie and Hongwei Li and Chengquan Guo and Ruizhe Jiang and Zhun Wang and Bo Li and Dawn Song and Wenbo Guo},
year={2025},
journal={arXiv preprint arXiv:2512.07533},
url={https://arxiv.org/abs/2512.07533},
}
data_utils.pydatasets/clean_dataset: die Trainingsdaten ohne PrimeVul
datasets/clean_dataset/python enthält die Daten von SVEN und SecCodePLTdatasets/clean_dataset/c enthält die Daten von Juliet und SVENdatasets/noisy_dataset
datasets/noisy_dataset/small_train: Enthält die Trainingsdaten von PrimeVul und SVEN mit ausgewählten CWEs (
wir verwenden die PrimeVul-Daten in diesem Datensatz als Training)datasets/noisy_dataset/large_train: Enthält die Trainingsdaten von PrimeVul, SVEN und SecCodePLT mit
mehr CWEs (Dieser Datensatz kann später zum Trainieren größerer Modelle verwendet werden)datasets/noisy_dataset/test: Ein kleiner, von Menschen verifizierter Testdatensatz aus PrimeVuldatasets/test
datasets/test/test_clean: Die Testdaten von SVEN, SecCodePLT und Juliet; mit OOD-CWEs, die nicht Teil des
Trainingssatzes sinddatasets/test/test_primevul_pair: Die ursprünglichen PrimeVul-Testdatenvulscan/data_process/data_utils/get_cwe_stat.py ausführen, um das Histogramm des Datensatzes zu erhalten| Dataset | Sprache | Training/Test | CWE | # Gutartig | # Anfällig | durchschnittliche Länge |
|---|
| Clean (seccodeplt) | Python | Train | 20 | 1281 | 1281 | 741 |
| Clean (juliet) | C/C++ | Train | 22 | 1716 | 1653 | 3689 |
| Hard (primevul filtered) | C/C++ | Train | 26 | 2717 | 2952 | 4689 |
| Long Context (Oss-fuzz) | C/C++ | Train | 3 | 475 | 604 | 12761 |
| Simple (seccodeplt) | Python | Test | 24 (6 ood) | 74 | 74 | 814 |
| Simple (juliet) | C/C++ | Test | 38 (14 ood) | 358 | 376 | 2575 |
| Hard (PrimeVul, SecLLMHolmes) | C/C++ | Test | 13 (5 ood) | 145 | 152 | 4545 |
| Long Context (Oss-fuzz) | C/C++ | Test | 3 (0 ood) | 0 | 320 | 18929 |
| primevul test (noisy) | C/C++ | Test | 56 (34 ood) | 421 | 422 | 5341 |