aboutsummaryrefslogtreecommitdiffstats
path: root/pkg/aflow/flow/patching/patching.go
blob: 7f2a81e22c9a821c6e2ee35d41b80fed8ae683e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
// Copyright 2025 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

package patching

import (
	"encoding/json"

	"github.com/google/syzkaller/pkg/aflow"
	"github.com/google/syzkaller/pkg/aflow/action/crash"
	"github.com/google/syzkaller/pkg/aflow/action/kernel"
	"github.com/google/syzkaller/pkg/aflow/ai"
	"github.com/google/syzkaller/pkg/aflow/tool/codeeditor"
	"github.com/google/syzkaller/pkg/aflow/tool/codeexpert"
	"github.com/google/syzkaller/pkg/aflow/tool/codesearcher"
	"github.com/google/syzkaller/pkg/aflow/tool/grepper"
)

type Inputs struct {
	ReproOpts    string
	ReproSyz     string
	ReproC       string
	KernelConfig string

	// Same as in the manager config.
	Syzkaller string
	Image     string
	Type      string
	VM        json.RawMessage

	// Use this fixed based kernel commit (for testing/local running).
	FixedBaseCommit string
	FixedRepository string
}

func createPatchingFlow(name string, summaryWindow int) *aflow.Flow {
	commonTools := aflow.Tools(codesearcher.Tools, grepper.Tool, codeexpert.Tool)
	return &aflow.Flow{
		Name: name,
		Root: aflow.Pipeline(
			baseCommitPicker,
			kernel.Checkout,
			kernel.Build,
			// Ensure we can reproduce the crash (and the build boots).
			crash.Reproduce,
			codesearcher.PrepareIndex,
			&aflow.LLMAgent{
				Name:          "debugger",
				Model:         aflow.BestExpensiveModel,
				Reply:         "BugExplanation",
				TaskType:      aflow.FormalReasoningTask,
				Instruction:   debuggingInstruction,
				Prompt:        debuggingPrompt,
				Tools:         commonTools,
				SummaryWindow: summaryWindow,
			},
			kernel.CheckoutScratch,
			&aflow.DoWhile{
				Do: aflow.Pipeline(
					&aflow.LLMAgent{
						Name:          "patch-generator",
						Model:         aflow.BestExpensiveModel,
						Reply:         "PatchExplanation",
						TaskType:      aflow.FormalReasoningTask,
						Instruction:   patchInstruction,
						Prompt:        patchPrompt,
						Tools:         aflow.Tools(commonTools, codeeditor.Tool),
						SummaryWindow: summaryWindow,
					},
					crash.TestPatch, // -> PatchDiff or TestError
				),
				While:         "TestError",
				MaxIterations: 10,
			},
			getMaintainers,
			getRecentCommits,
			&aflow.LLMAgent{
				Name:          "description-generator",
				Model:         aflow.BestExpensiveModel,
				Reply:         "PatchDescription",
				TaskType:      aflow.FormalReasoningTask,
				Instruction:   descriptionInstruction,
				Prompt:        descriptionPrompt,
				Tools:         commonTools,
				SummaryWindow: summaryWindow,
			},
		),
	}
}

func init() {
	aflow.Register[Inputs, ai.PatchingOutputs](
		ai.WorkflowPatching,
		"generate a kernel patch fixing a provided bug reproducer",
		createPatchingFlow("", 0),
		createPatchingFlow("summary", 10),
	)
}

// TODO: mention not doing assumptions about the source code, and instead querying code using tools.
// TODO: mention to extensively use provided tools to confirm everything.
// TODO: use cause bisection info, if available.

const debuggingInstruction = `
You are an experienced Linux kernel developer tasked with debugging a kernel crash root cause.
You need to provide a detailed explanation of the root cause for another developer to be
able to write a fix for the bug based on your explanation. Include all relevant details
into the response: function/struct/field/etc names, code snippets, line numbers,
macro/enum values, etc.

{{if titleIsKASANNullDeref .BugTitle}}
Note: under KASAN NULL-derefs on the source level don't happen around the actual 0 address,
they happen on the KASAN shadow memory around address dfff800000000000 or dffffc0000000000.
Don't be confused by that. Look for the like at the top of the report that tells
the access address and size.
{{end}}
`

const debuggingPrompt = `
The crash is:

{{.CrashReport}}
`

const patchInstruction = `
You are an experienced Linux kernel developer tasked with creating a fix for a kernel bug.
You will be given a crash report, and an initial explanation of the root cause done by another
kernel expert.

Use the codeedit tool to do code edits.
Note: you will not see your changes when looking at the code using codesearch tools.

Your final reply should contain explanation of what you did in the patch and why
(details not present in the initial explanation of the bug).

Your fix must not just prevent the given crash, but also be the best fix for the underlying
root cause from the software engineering point of view. There can be several ways to fix the
same bug. Consider alternatives, and pick the best one. For example, additional checks may be
added at different locations/functions, it's usually better to place them earlier in the
execution to avoid multiple checks at various locations later.

Frequently the same coding mistake is done in several locations in the source code.
Check if your fix should be extended/applied to similar cases around to fix other similar bugs.
But don't go too wide, don't try to fix problems kernel-wide, fix similar issues
in the same file only.

If you are changing post-conditions of a function, consider all callers of the functions,
and if they need to be updated to handle new post-conditions. For example, if you make
a function that previously never returned a NULL, return NULL, consider if callers
need to be updated to handle NULL return value.

{{if titleIsWarning .BugTitle}}
If you will end up removing the WARN_ON macro because the condition can legitimately happen,
add a pr_err call that logs that the unlikely condition has happened. The pr_err message
must not include "WARNING" string.
{{end}}
`

const patchPrompt = `
The crash that corresponds to the bug is:

{{.CrashReport}}

The explanation of the root cause of the bug is:

{{.BugExplanation}}

{{if .TestError}}

Another developer tried to fix this bug, and come up with the following strategy for fixing:

{{.PatchExplanation}}

{{/* A TestError without PatchDiff means the previous invocation did not generate any patch. */}}
{{if .PatchDiff}}
and the following patch:

{{.PatchDiff}}

However, the patch testing failed with the following error:

{{.TestError}}

If the error is fixable, and the fix patch is correct overall,
the create a new fixed patch based on the provided one with the errors fixed.
If the error points to a fundamental issue with the approach in the patch,
then create a new patch from scratch.
Note: in both cases the source tree does not contain the patch yet
(so if you want to create a new fixed patch, you need to recreate it
in its entirety from scratch using the codeeditor tool).
{{else}}
If the strategy looks reasonable to you, proceed with patch generation.
{{end}}
{{end}}
`

const descriptionInstruction = `
You are an experienced Linux kernel developer tasked with writing a commit description for
a kernel bug fixing commit. The description should start with a one-line summary,
and then include description of the bug being fixed, and how it's fixed by the provided patch.

Your final reply should contain only the text of the commit description.
Phrase the one-line summary so that it is not longer than 72 characters.
The rest of the description must be word-wrapped at 72 characters.
`

const descriptionPrompt = `
The crash that corresponds to the bug is:

{{.CrashReport}}

The explanation of the root cause of the bug is:

{{.BugExplanation}}

The diff of the bug fix is:

{{.PatchDiff}}

Additional description of the patch:

{{.PatchExplanation}}

Here are summaries of recent commits that touched the same files.
Format the summary line consistently with these, look how prefixes
are specified, letter capitalization, style, etc. 

{{.RecentCommits}}

{{if titleIsWarning .BugTitle}}
If the patch removes the WARN_ON macro, refer to the fact that WARN_ON
must not be used for conditions that can legitimately happen, and that pr_err
should be used instead if necessary.
{{end}}
`