Skip to content

Commit aeef684

Browse files
committed
pkg/architecture: Add sandboxed binfmt_misc registration support
Cross-architecture containers need QEMU binfmt_misc handlers registered within the container so that non-native architecture binaries can be executed via the host's kernel. Add the Registration struct that models a binfmt_misc registration entry, including name, magic type, offset, ELF magic/mask bytes, interpreter path, and flags. Add functions: - MountBinfmtMisc() to mount the sanboxed binfmt_misc filesystem inside a container, which enables setting the C flag in binfmt_misc registration without affecting the host system. The C flag presents a threat of privilege escalation when registered on the host, that why we want to have it isolated [1] - getDefaultRegistration() to fill a Registration struct containing all necessary binfmt_misc information taken from the architecture.supportedArchitectures data - RegisterBinfmtMisc() to write the registration string to /proc/sys/fs/binfmt_misc/register, which makes the non-native binary perception active - bytesToEscapedString() helper that formats byte slices into the \xHH-escaped string format required by the binfmt_misc register interface [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=21ca59b365c0 #1782 Signed-off-by: Dalibor Kricka <dalidalk@seznam.cz>
1 parent 7a6fa20 commit aeef684

1 file changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright © 2019 – 2026 Red Hat Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package architecture
18+
19+
import (
20+
"bytes"
21+
"fmt"
22+
"os"
23+
"path/filepath"
24+
"strings"
25+
26+
"github.com/containers/toolbox/pkg/shell"
27+
"github.com/sirupsen/logrus"
28+
)
29+
30+
type Registration struct {
31+
Name string
32+
MagicType string
33+
Offset string
34+
Magic []byte
35+
Mask []byte
36+
Interpreter string
37+
Flags string
38+
}
39+
40+
const (
41+
defaultMagicType = "M"
42+
defaultFlags = "FC"
43+
defaultOffset = "0"
44+
binfmtMiscPath = "/proc/sys/fs/binfmt_misc"
45+
)
46+
47+
func (r *Registration) buildRegistrationString() string {
48+
return fmt.Sprintf(":%s:%s:%s:%s:%s:%s:%s",
49+
r.Name, r.MagicType, r.Offset,
50+
bytesToEscapedString(r.Magic),
51+
bytesToEscapedString(r.Mask),
52+
r.Interpreter, r.Flags)
53+
}
54+
55+
func (r *Registration) register() error {
56+
logrus.Debugf("Registering binfmt_misc for %s", r.Name)
57+
58+
regString := r.buildRegistrationString()
59+
logrus.Debugf("Registration string: %s", regString)
60+
61+
if err := os.WriteFile(filepath.Join(binfmtMiscPath, "register"), []byte(regString), 0200); err != nil {
62+
return fmt.Errorf("failed to register binfmt_misc handler: %w", err)
63+
}
64+
return nil
65+
}
66+
67+
func bytesToEscapedString(bytes []byte) string {
68+
var result strings.Builder
69+
for _, b := range bytes {
70+
result.WriteString(fmt.Sprintf("\\x%02x", b))
71+
}
72+
return result.String()
73+
}
74+
75+
func getDefaultRegistration(archID int, interpreterPath string) *Registration {
76+
arch, exists := getArchitecture(archID)
77+
if !exists {
78+
return nil
79+
}
80+
81+
var name string
82+
flags := defaultFlags
83+
magicType := defaultMagicType
84+
offset := defaultOffset
85+
86+
if arch.BinfmtName != "" {
87+
name = arch.BinfmtName
88+
} else {
89+
name = "qemu-" + arch.NameBinfmt
90+
}
91+
92+
if arch.BinfmtFlags != "" {
93+
flags = arch.BinfmtFlags
94+
}
95+
96+
if arch.BinfmtMagicType != "" {
97+
magicType = arch.BinfmtMagicType
98+
}
99+
100+
if arch.BinfmtOffset != "" {
101+
offset = arch.BinfmtOffset
102+
}
103+
104+
interpreter := interpreterPath
105+
if !strings.HasPrefix(interpreterPath, "/run/host/") {
106+
interpreter = filepath.Join("/run/host", interpreter)
107+
}
108+
109+
return &Registration{
110+
Name: name,
111+
MagicType: magicType,
112+
Offset: offset,
113+
Magic: arch.ELFMagic,
114+
Mask: arch.ELFMask,
115+
Interpreter: interpreter,
116+
Flags: flags,
117+
}
118+
}
119+
120+
func MountBinfmtMisc() error {
121+
args := []string{
122+
"binfmt_misc",
123+
"-t",
124+
"binfmt_misc",
125+
binfmtMiscPath,
126+
}
127+
128+
var stdout bytes.Buffer
129+
130+
if err := shell.Run("mount", nil, &stdout, nil, args...); err != nil {
131+
return fmt.Errorf("failed to mount binfmt_misc: %w", err)
132+
}
133+
134+
logrus.Debugf("Result of mount command: %s", stdout.String())
135+
136+
return nil
137+
}
138+
139+
func RegisterBinfmtMisc(archID int, interpreterPath string) error {
140+
reg := getDefaultRegistration(archID, interpreterPath)
141+
if reg == nil {
142+
logrus.Debugf("Unable to register binfmt_misc for architecture '%s'", GetArchNameOCI(archID))
143+
return fmt.Errorf("Toolbx does not support architecture '%s'", GetArchNameOCI(archID))
144+
}
145+
146+
if err := reg.register(); err != nil {
147+
return err
148+
}
149+
150+
return nil
151+
}

0 commit comments

Comments
 (0)