xref: /aosp_15_r20/external/boringssl/src/util/make_errors.go (revision 8fb009dc861624b67b6cdb62ea21f0f22d0c584b)
1// Copyright (c) 2014, Google Inc.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15//go:build ignore
16
17package main
18
19import (
20	"bufio"
21	"errors"
22	"flag"
23	"fmt"
24	"io"
25	"os"
26	"path/filepath"
27	"sort"
28	"strconv"
29	"strings"
30)
31
32// ssl.h reserves values 1000 and above for error codes corresponding to
33// alerts. If automatically assigned reason codes exceed this value, this script
34// will error. This must be kept in sync with SSL_AD_REASON_OFFSET in ssl.h.
35const reservedReasonCode = 1000
36
37var resetFlag *bool = flag.Bool("reset", false, "If true, ignore current assignments and reassign from scratch")
38
39type libraryInfo struct {
40	sourceDirs []string
41	headerName string
42}
43
44func getLibraryInfo(lib string) libraryInfo {
45	var info libraryInfo
46	if lib == "ssl" {
47		info.sourceDirs = []string{"ssl"}
48	} else {
49		info.sourceDirs = []string{
50			filepath.Join("crypto", lib),
51			filepath.Join("crypto", lib+"_extra"),
52			filepath.Join("crypto", "fipsmodule", lib),
53		}
54	}
55	info.headerName = lib + ".h"
56
57	if lib == "evp" {
58		info.headerName = "evp_errors.h"
59		info.sourceDirs = append(info.sourceDirs, filepath.Join("crypto", "hpke"))
60	}
61
62	if lib == "x509v3" {
63		info.headerName = "x509v3_errors.h"
64		info.sourceDirs = append(info.sourceDirs, filepath.Join("crypto", "x509"))
65	}
66
67	return info
68}
69
70func makeErrors(lib string, reset bool) error {
71	topLevelPath, err := findToplevel()
72	if err != nil {
73		return err
74	}
75
76	info := getLibraryInfo(lib)
77
78	headerPath := filepath.Join(topLevelPath, "include", "openssl", info.headerName)
79	errDir := filepath.Join(topLevelPath, "crypto", "err")
80	dataPath := filepath.Join(errDir, lib+".errordata")
81
82	headerFile, err := os.Open(headerPath)
83	if err != nil {
84		if os.IsNotExist(err) {
85			return fmt.Errorf("No header %s. Run in the right directory or touch the file.", headerPath)
86		}
87
88		return err
89	}
90
91	prefix := strings.ToUpper(lib)
92	reasons, err := parseHeader(prefix, headerFile)
93	headerFile.Close()
94
95	if reset {
96		err = nil
97		// Retain any reason codes above reservedReasonCode.
98		newReasons := make(map[string]int)
99		for key, value := range reasons {
100			if value >= reservedReasonCode {
101				newReasons[key] = value
102			}
103		}
104		reasons = newReasons
105	}
106
107	if err != nil {
108		return err
109	}
110
111	for _, sourceDir := range info.sourceDirs {
112		fullPath := filepath.Join(topLevelPath, sourceDir)
113		dir, err := os.Open(fullPath)
114		if err != nil {
115			if os.IsNotExist(err) {
116				// Some directories in the search path may not exist.
117				continue
118			}
119			return err
120		}
121		defer dir.Close()
122		filenames, err := dir.Readdirnames(-1)
123		if err != nil {
124			return err
125		}
126
127		for _, name := range filenames {
128			if !strings.HasSuffix(name, ".c") && !strings.HasSuffix(name, ".cc") {
129				continue
130			}
131
132			if err := addReasons(reasons, filepath.Join(fullPath, name), prefix); err != nil {
133				return err
134			}
135		}
136	}
137
138	assignNewValues(reasons, reservedReasonCode)
139
140	headerFile, err = os.Open(headerPath)
141	if err != nil {
142		return err
143	}
144	defer headerFile.Close()
145
146	newHeaderFile, err := os.OpenFile(headerPath+".tmp", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0666)
147	if err != nil {
148		return err
149	}
150	defer newHeaderFile.Close()
151
152	if err := writeHeaderFile(newHeaderFile, headerFile, prefix, reasons); err != nil {
153		return err
154	}
155	// Windows forbids renaming an open file.
156	headerFile.Close()
157	newHeaderFile.Close()
158	if err := os.Rename(headerPath+".tmp", headerPath); err != nil {
159		return err
160	}
161
162	dataFile, err := os.OpenFile(dataPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
163	if err != nil {
164		return err
165	}
166
167	outputStrings(dataFile, lib, reasons)
168	dataFile.Close()
169
170	return nil
171}
172
173func findToplevel() (path string, err error) {
174	path = "."
175	buildingPath := filepath.Join(path, "BUILDING.md")
176
177	_, err = os.Stat(buildingPath)
178	for i := 0; i < 2 && err != nil && os.IsNotExist(err); i++ {
179		if i == 0 {
180			path = ".."
181		} else {
182			path = filepath.Join("..", path)
183		}
184		buildingPath = filepath.Join(path, "BUILDING.md")
185		_, err = os.Stat(buildingPath)
186	}
187	if err != nil {
188		return "", errors.New("Cannot find BUILDING.md file at the top-level")
189	}
190	return path, nil
191}
192
193type assignment struct {
194	key   string
195	value int
196}
197
198func outputAssignments(w io.Writer, assignments map[string]int) {
199	sorted := make([]assignment, 0, len(assignments))
200	for key, value := range assignments {
201		sorted = append(sorted, assignment{key, value})
202	}
203
204	sort.Slice(sorted, func(i, j int) bool { return sorted[i].value < sorted[j].value })
205
206	for _, assignment := range sorted {
207		fmt.Fprintf(w, "#define %s %d\n", assignment.key, assignment.value)
208	}
209}
210
211func parseDefineLine(line, lib string) (key string, value int, ok bool) {
212	if !strings.HasPrefix(line, "#define ") {
213		return
214	}
215
216	fields := strings.Fields(line)
217	if len(fields) != 3 {
218		return
219	}
220
221	key = fields[1]
222	if !strings.HasPrefix(key, lib+"_R_") {
223		return
224	}
225
226	var err error
227	if value, err = strconv.Atoi(fields[2]); err != nil {
228		return
229	}
230
231	ok = true
232	return
233}
234
235func writeHeaderFile(w io.Writer, headerFile io.Reader, lib string, reasons map[string]int) error {
236	var last []byte
237	var haveLast, sawDefine bool
238	newLine := []byte("\n")
239
240	scanner := bufio.NewScanner(headerFile)
241	for scanner.Scan() {
242		line := scanner.Text()
243		_, _, ok := parseDefineLine(line, lib)
244		if ok {
245			sawDefine = true
246			continue
247		}
248
249		if haveLast {
250			w.Write(last)
251			w.Write(newLine)
252		}
253
254		if len(line) > 0 || !sawDefine {
255			last = []byte(line)
256			haveLast = true
257		} else {
258			haveLast = false
259		}
260		sawDefine = false
261	}
262
263	if err := scanner.Err(); err != nil {
264		return err
265	}
266
267	outputAssignments(w, reasons)
268	w.Write(newLine)
269
270	if haveLast {
271		w.Write(last)
272		w.Write(newLine)
273	}
274
275	return nil
276}
277
278func outputStrings(w io.Writer, lib string, assignments map[string]int) {
279	lib = strings.ToUpper(lib)
280	prefixLen := len(lib + "_R_")
281
282	keys := make([]string, 0, len(assignments))
283	for key := range assignments {
284		keys = append(keys, key)
285	}
286	sort.Strings(keys)
287
288	for _, key := range keys {
289		fmt.Fprintf(w, "%s,%d,%s\n", lib, assignments[key], key[prefixLen:])
290	}
291}
292
293func assignNewValues(assignments map[string]int, reserved int) {
294	// Needs to be in sync with the reason limit in
295	// |ERR_reason_error_string|.
296	max := 99
297
298	for _, value := range assignments {
299		if reserved >= 0 && value >= reserved {
300			continue
301		}
302		if value > max {
303			max = value
304		}
305	}
306
307	max++
308
309	// Sort the keys, so this script is reproducible.
310	keys := make([]string, 0, len(assignments))
311	for key, value := range assignments {
312		if value == -1 {
313			keys = append(keys, key)
314		}
315	}
316	sort.Strings(keys)
317
318	for _, key := range keys {
319		if reserved >= 0 && max >= reserved {
320			// If this happens, try passing -reset. Otherwise bump
321			// up reservedReasonCode.
322			panic("Automatically-assigned values exceeded limit!")
323		}
324		assignments[key] = max
325		max++
326	}
327}
328
329func handleDeclareMacro(line, prefix, join, macroName string, m map[string]int) {
330	if i := strings.Index(line, macroName); i >= 0 {
331		contents := line[i+len(macroName):]
332		if i := strings.Index(contents, ")"); i >= 0 {
333			contents = contents[:i]
334			args := strings.Split(contents, ",")
335			for i := range args {
336				args[i] = strings.TrimSpace(args[i])
337			}
338			if len(args) != 2 {
339				panic("Bad macro line: " + line)
340			}
341			if args[0] == prefix {
342				token := args[0] + join + args[1]
343				if _, ok := m[token]; !ok {
344					m[token] = -1
345				}
346			}
347		}
348	}
349}
350
351func addReasons(reasons map[string]int, filename, prefix string) error {
352	file, err := os.Open(filename)
353	if err != nil {
354		return err
355	}
356	defer file.Close()
357
358	reasonPrefix := prefix + "_R_"
359
360	scanner := bufio.NewScanner(file)
361	for scanner.Scan() {
362		line := scanner.Text()
363
364		handleDeclareMacro(line, prefix, "_R_", "OPENSSL_DECLARE_ERROR_REASON(", reasons)
365
366		for len(line) > 0 {
367			i := strings.Index(line, prefix+"_")
368			if i == -1 {
369				break
370			}
371
372			line = line[i:]
373			end := strings.IndexFunc(line, func(r rune) bool {
374				return !(r == '_' || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9'))
375			})
376			if end == -1 {
377				end = len(line)
378			}
379
380			var token string
381			token, line = line[:end], line[end:]
382
383			switch {
384			case strings.HasPrefix(token, reasonPrefix):
385				if _, ok := reasons[token]; !ok {
386					reasons[token] = -1
387				}
388			}
389		}
390	}
391
392	return scanner.Err()
393}
394
395func parseHeader(lib string, file io.Reader) (reasons map[string]int, err error) {
396	reasons = make(map[string]int)
397
398	scanner := bufio.NewScanner(file)
399	for scanner.Scan() {
400		key, value, ok := parseDefineLine(scanner.Text(), lib)
401		if !ok {
402			continue
403		}
404
405		reasons[key] = value
406	}
407
408	err = scanner.Err()
409	return
410}
411
412func main() {
413	flag.Parse()
414	if flag.NArg() == 0 {
415		fmt.Fprintf(os.Stderr, "Usage: make_errors.go LIB [LIB2...]\n")
416		os.Exit(1)
417	}
418
419	for _, lib := range flag.Args() {
420		if err := makeErrors(lib, *resetFlag); err != nil {
421			fmt.Fprintf(os.Stderr, "Error generating errors for %q: %s\n", lib, err)
422			os.Exit(1)
423		}
424	}
425}
426