19
// registeredBinfmtMisc walks /proc/sys/fs/binfmt_misc and iterates through a
20
// list of known ELF header values to see if there's an emulator registered for
21
// them. Returns the list of emulated targets (which may be empty), or an
22
// error if something unexpected happened.
23
func registeredBinfmtMisc() ([]string, error) {
24
var registered []string
25
globalEnabled := false
26
err := filepath.WalkDir("/proc/sys/fs/binfmt_misc", func(path string, d fs.DirEntry, err error) error {
27
if filepath.Base(path) == "register" { // skip this one
30
if err != nil && !errors.Is(err, os.ErrNotExist) {
37
if !info.Mode().IsRegular() {
38
return nil // skip the directory itself
40
f, err := os.Open(path)
45
if filepath.Base(path) == "status" {
46
b, err := io.ReadAll(f)
50
status := strings.TrimSpace(string(b))
57
return fmt.Errorf("unrecognized binfmt_misc status value %q in %q", status, path)
61
offset, magic, mask, err := parseBinfmtMisc(path, f)
68
for platform, headers := range getKnownELFPlatformHeaders() {
69
for _, header := range headers {
70
if magicMatch(header, offset, mask, magic) {
71
registered = append(registered, platform)
81
sort.Strings(registered)
82
return registered, err
85
// magicMatch compares header, starting at the specified offset, masked with
86
// mask, against the magic value
87
func magicMatch(header []byte, offset int, mask, magic []byte) bool {
89
for i := offset; i < offset+len(magic); i++ {
94
if len(mask) > i-offset {
103
return mismatch >= offset+len(magic)
106
// parseBinfmtMisc parses a binfmt_misc registry entry. It returns the offset,
107
// magic, and mask values, or an error if there was an error parsing the data.
108
// If the returned offset is negative, the entry was disabled or should be
109
// non-fatally ignored for some other reason.
110
func parseBinfmtMisc(path string, r io.Reader) (int, []byte, []byte, error) {
112
magicString, maskString := "", ""
113
scanner := bufio.NewScanner(r)
115
text := scanner.Text()
116
if strings.TrimSpace(text) == "" {
119
fields := strings.Fields(text)
122
return -1, nil, nil, nil // we should ignore this specific one
123
case "enabled": // keep scanning this entry
124
case "interpreter": // good, but not something we need to record
126
if len(fields) != 2 {
127
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
129
offset64, err := strconv.ParseInt(fields[1], 10, 8)
131
return -1, nil, nil, fmt.Errorf("invalid offset %q in %q", fields[1], path)
133
offset = int(offset64)
135
if len(fields) != 2 {
136
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
138
magicString = fields[1]
140
if len(fields) != 2 {
141
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
143
maskString = fields[1]
144
case "flags", "flags:":
145
if len(fields) != 2 {
146
return -1, nil, nil, fmt.Errorf("invalid format for %q in %q", text, path)
148
if !strings.Contains(fields[1], "F") { // won't work in other mount namespaces, so ignore it
149
return -1, nil, nil, nil
152
return -1, nil, nil, fmt.Errorf("unrecognized field %q in %q", fields[0], path)
156
if magicString == "" || maskString == "" { // entry is missing some info we need here
157
return -1, nil, nil, nil
159
magic, err := hex.DecodeString(magicString)
161
return -1, nil, nil, fmt.Errorf("invalid magic %q in %q", magicString, path)
163
mask, err := hex.DecodeString(maskString)
165
return -1, nil, nil, fmt.Errorf("invalid mask %q in %q", maskString, path)
167
return offset, magic, mask, nil