Skip to content

Commit 96831b5

Browse files
committed
fix: avoid CGO getgrgid_r segfault in static Linux binaries
Signed-off-by: Nancy <9d.24.nancy.sangani@gmail.com>
1 parent 119b621 commit 96831b5

2 files changed

Lines changed: 38 additions & 8 deletions

File tree

.github/workflows/release.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
GOARCH: ${{ matrix.goarch }}
7878
run: |
7979
go build \
80-
-tags "static system_libgit2 enable_libgit2" \
80+
-tags "static system_libgit2 enable_libgit2 osusergo netgo" \
8181
-ldflags "-X github.com/modelpack/modctl/pkg/version.GitVersion=${{ github.ref_name }} \
8282
-X github.com/modelpack/modctl/pkg/version.GitCommit=$(git rev-parse --short HEAD) \
8383
-X github.com/modelpack/modctl/pkg/version.BuildTime=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
@@ -155,4 +155,4 @@ jobs:
155155
checksums.txt
156156
generate_release_notes: true
157157
env:
158-
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}
158+
GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }}

pkg/archiver/archiver.go

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,39 @@ import (
2323
"os"
2424
"path/filepath"
2525
"strings"
26+
"syscall"
2627
)
2728

29+
// buildTarHeader creates a tar header from FileInfo without using CGO-based
30+
// os/user.LookupGroupId or os/user.LookupUserId. This avoids a segfault in
31+
// statically linked CGO binaries caused by glibc NSS (getgrgid_r) being
32+
// incompatible with static linking across different glibc versions.
33+
// See: https://github.com/modelpack/modctl/issues/285
34+
func buildTarHeader(info os.FileInfo) (*tar.Header, error) {
35+
header := &tar.Header{
36+
Name: info.Name(),
37+
Size: info.Size(),
38+
Mode: int64(info.Mode()),
39+
ModTime: info.ModTime(),
40+
}
41+
42+
// Set file type flag.
43+
if info.IsDir() {
44+
header.Typeflag = tar.TypeDir
45+
} else {
46+
header.Typeflag = tar.TypeReg
47+
}
48+
49+
// Safely extract UID/GID from syscall.Stat_t without CGO user/group name lookup.
50+
// We intentionally leave Uname/Gname empty to avoid os/user CGO calls entirely.
51+
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
52+
header.Uid = int(stat.Uid)
53+
header.Gid = int(stat.Gid)
54+
}
55+
56+
return header, nil
57+
}
58+
2859
// Tar creates a tar archive of the specified path (file or directory)
2960
// and returns the content as a stream. For individual files, it preserves
3061
// the directory structure relative to the working directory.
@@ -56,7 +87,7 @@ func Tar(srcPath string, workDir string) (io.Reader, error) {
5687
return fmt.Errorf("failed to get relative path: %w", err)
5788
}
5889

59-
header, err := tar.FileInfoHeader(info, "")
90+
header, err := buildTarHeader(info)
6091
if err != nil {
6192
return fmt.Errorf("failed to create tar header: %w", err)
6293
}
@@ -95,14 +126,13 @@ func Tar(srcPath string, workDir string) (io.Reader, error) {
95126
}
96127
defer file.Close()
97128

98-
header, err := tar.FileInfoHeader(info, "")
129+
header, err := buildTarHeader(info)
99130
if err != nil {
100131
pw.CloseWithError(fmt.Errorf("failed to create tar header: %w", err))
101132
return
102133
}
103134

104-
// Use relative path as the header name to preserve directory structure
105-
// This keeps the directory structure as part of the file path in the tar.
135+
// Use relative path as the header name to preserve directory structure.
106136
relPath, err := filepath.Rel(workDir, srcPath)
107137
if err != nil {
108138
pw.CloseWithError(fmt.Errorf("failed to get relative path: %w", err))
@@ -189,9 +219,9 @@ func Untar(reader io.Reader, destPath string) error {
189219
}
190220
file.Close()
191221

192-
// Set correct permissions for the directory.
222+
// Set correct permissions for the file.
193223
if err := os.Chmod(targetPath, os.FileMode(header.Mode)); err != nil {
194-
return fmt.Errorf("failed to set directory permissions %s: %w", targetPath, err)
224+
return fmt.Errorf("failed to set file permissions %s: %w", targetPath, err)
195225
}
196226
// Set modification time for the file.
197227
if err := os.Chtimes(targetPath, header.ModTime, header.ModTime); err != nil {

0 commit comments

Comments
 (0)