Skip to content

Commit 8f472c0

Browse files
authored
Merge pull request #300 from jgehrcke/jp/major-parser
Make /proc/devices parser more robust (fix #297)
2 parents 3102d7f + d0617be commit 8f472c0

File tree

1 file changed

+35
-35
lines changed
  • cmd/compute-domain-kubelet-plugin

1 file changed

+35
-35
lines changed

cmd/compute-domain-kubelet-plugin/nvlib.go

Lines changed: 35 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"os"
2323
"os/exec"
2424
"path/filepath"
25+
"regexp"
2526
"strconv"
2627
"strings"
2728

@@ -225,48 +226,47 @@ func (l deviceLib) getImexChannelCount() (int, error) {
225226
return 2048, nil
226227
}
227228

229+
// getDeviceMajor searches for one "<integer> <name>" occurrence in the
230+
// "Character devices" section of the /proc/devices file, and returns the
231+
// integer.
228232
func (l deviceLib) getDeviceMajor(name string) (int, error) {
229-
file, err := os.Open(procDevicesPath)
230-
if err != nil {
231-
return -1, err
232-
}
233-
defer file.Close()
234-
235-
scanner := bufio.NewScanner(file)
236-
foundCharDevices := false
237233

238-
for scanner.Scan() {
239-
line := scanner.Text()
234+
re := regexp.MustCompile(
235+
// The `(?s)` flag makes `.` match newlines. The greedy modifier in
236+
// `.*?` ensures to pick the first match after "Character devices".
237+
// Extract the number as capture group (the first and only group).
238+
"(?s)Character devices:.*?" +
239+
"([0-9]+) " + regexp.QuoteMeta(name) +
240+
".*Block devices:",
241+
)
240242

241-
// Ignore empty lines
242-
if line == "" {
243-
continue
244-
}
243+
data, err := os.ReadFile(procDevicesPath)
244+
if err != nil {
245+
return -1, fmt.Errorf("error reading '%s': %w", procDevicesPath, err)
246+
}
245247

246-
// Check for any line with text followed by a colon (header)
247-
if strings.Contains(line, ":") {
248-
// Stop if we've already found the character devices section and reached another section
249-
if foundCharDevices {
250-
break
251-
}
252-
// Check if we entered the character devices section
253-
if strings.HasSuffix(line, ":") && strings.HasPrefix(line, "Character") {
254-
foundCharDevices = true
255-
}
256-
// Continue to the next line, regardless
257-
continue
258-
}
248+
// Expect precisely one match: first element is the total match, second
249+
// element corresponds to first capture group within that match (i.e., the
250+
// number of interest).
251+
matches := re.FindStringSubmatch(string(data))
252+
if len(matches) != 2 {
253+
return -1, fmt.Errorf("error parsing '%s': unexpected regex match: %v", procDevicesPath, matches)
254+
}
259255

260-
// If we've passed the character devices section, check for nvidiaCapsImexChannelsDeviceName
261-
if foundCharDevices {
262-
parts := strings.Fields(line)
263-
if len(parts) == 2 && parts[1] == name {
264-
return strconv.Atoi(parts[0])
265-
}
266-
}
256+
// Convert capture group content to integer. Perform upper bound check:
257+
// value must fit into 32-bit integer (it's then also guaranteed to fit into
258+
// a 32-bit unsigned integer, which is the type that must be passed to
259+
// unix.Mkdev()).
260+
major, err := strconv.ParseInt(matches[1], 10, 32)
261+
if err != nil {
262+
return -1, fmt.Errorf("int conversion failed for '%v': %w", matches[1], err)
267263
}
268264

269-
return -1, scanner.Err()
265+
// ParseInt() always returns an integer of explicit type `int64`. We have
266+
// performed an upper bound check so it's safe to convert this to `int`
267+
// (which is documented as "int is a signed integer type that is at least 32
268+
// bits in size", so in theory it could be smaller than int64).
269+
return int(major), nil
270270
}
271271

272272
func (l deviceLib) parseNVCapDeviceInfo(nvcapsFilePath string) (*nvcapDeviceInfo, error) {

0 commit comments

Comments
 (0)