Skip to content

Commit feafe87

Browse files
migrated ebpf lib from gobpf to cilium
Up until now we've been using https://github.com/iovisor/gobpf/ for intercepting events from the kernel, and it has worked very well. Unfortunately it's deprecated since 2021 (iovisor/gobpf#304), which means that some of the known issues and limitations won't be fixed. After evaluating some options, I've migrated the code from gobpf to github.com/cilium/ebpf. More info: #1222, #1312
1 parent 297ffbb commit feafe87

File tree

15 files changed

+557
-402
lines changed

15 files changed

+557
-402
lines changed

daemon/core/ebpf.go

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ package core
33
import (
44
"fmt"
55

6+
"github.com/cilium/ebpf"
67
"github.com/evilsocket/opensnitch/daemon/log"
7-
"github.com/iovisor/gobpf/elf"
88
)
99

1010
// LoadEbpfModule loads the given eBPF module, from the given path if specified.
1111
// Otherwise t'll try to load the module from several default paths.
12-
func LoadEbpfModule(module, path string) (m *elf.Module, err error) {
12+
func LoadEbpfModule(module, path string) (m *ebpf.Collection, err error) {
1313
var (
1414
modulesDir = "/opensnitchd/ebpf"
1515
paths = []string{
@@ -28,22 +28,37 @@ func LoadEbpfModule(module, path string) (m *elf.Module, err error) {
2828
moduleError := fmt.Errorf(`Module not found (%s) in any of the paths.
2929
You may need to install the corresponding package`, module)
3030

31+
logLevel := ebpf.LogLevel(0)
32+
if log.GetLogLevel() == log.DEBUG {
33+
logLevel = (ebpf.LogLevelBranch | ebpf.LogLevelInstruction | ebpf.LogLevelStats)
34+
}
35+
collOpts := ebpf.CollectionOptions{
36+
Programs: ebpf.ProgramOptions{LogLevel: logLevel},
37+
}
38+
3139
for _, p := range paths {
3240
modulePath = fmt.Sprint(p, "/", module)
3341
log.Debug("[eBPF] trying to load %s", modulePath)
3442
if !Exists(modulePath) {
3543
continue
3644
}
37-
m = elf.NewModule(modulePath)
38-
39-
if m.Load(nil) == nil {
40-
log.Info("[eBPF] module loaded: %s", modulePath)
41-
return m, nil
45+
specs, err := ebpf.LoadCollectionSpec(modulePath)
46+
if err != nil {
47+
log.Error("[eBPF] module specs error: %s", err)
48+
continue
4249
}
43-
moduleError = fmt.Errorf(`
50+
m, err := ebpf.NewCollectionWithOptions(specs, collOpts)
51+
if err != nil {
52+
log.Error("[eBPF] module collection error: %s", err)
53+
continue
54+
}
55+
56+
log.Info("[eBPF] module loaded: %s", modulePath)
57+
return m, nil
58+
}
59+
moduleError = fmt.Errorf(`
4460
unable to load eBPF module (%s). Your kernel version (%s) might not be compatible.
4561
If this error persists, change process monitor method to 'proc'`, module, GetKernelVersion())
46-
}
4762

4863
return m, moduleError
4964
}

daemon/dns/ebpfhook.go

Lines changed: 98 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ import (
99
"net"
1010
"os"
1111
"os/signal"
12-
"strings"
12+
"runtime"
1313
"syscall"
1414
"time"
1515

16+
"github.com/cilium/ebpf"
17+
"github.com/cilium/ebpf/link"
18+
"github.com/cilium/ebpf/ringbuf"
1619
"github.com/evilsocket/opensnitch/daemon/core"
1720
"github.com/evilsocket/opensnitch/daemon/log"
18-
bpf "github.com/iovisor/gobpf/elf"
1921
)
2022

2123
/*
@@ -49,6 +51,7 @@ char* find_libc() {
4951
break;
5052
}
5153
54+
//printf("map->l_name: %s\n", map->l_name);
5255
if(strstr(map->l_name, "libc.so")){
5356
fprintf(stderr,"found %s\n", map->l_name);
5457
return map->l_name;
@@ -57,8 +60,6 @@ char* find_libc() {
5760
}
5861
return NULL;
5962
}
60-
61-
6263
*/
6364
import "C"
6465

@@ -68,6 +69,25 @@ type nameLookupEvent struct {
6869
Host [252]byte
6970
}
7071

72+
// ProbeDefs holds the hooks defined in the module
73+
type ProbeDefs struct {
74+
URProbeGethostByname *ebpf.Program `ebpf:"uretprobe__gethostbyname"`
75+
UProbeGetAddrinfo *ebpf.Program `ebpf:"uprobe__getaddrinfo"`
76+
URProbeGetAddrinfo *ebpf.Program `ebpf:"uretprobe__getaddrinfo"`
77+
}
78+
79+
// MapDefs holds the maps defined in the module
80+
type MapDefs struct {
81+
// BPF_MAP_TYPE_RINGBUF
82+
PerfEvents *ebpf.Map `ebpf:"events"`
83+
}
84+
85+
// container of hooks and maps
86+
type dnsDefsT struct {
87+
ProbeDefs
88+
MapDefs
89+
}
90+
7191
func findLibc() (string, error) {
7292
ret := C.find_libc()
7393

@@ -95,83 +115,120 @@ func lookupSymbol(elffile *elf.File, symbolName string) (uint64, error) {
95115

96116
// ListenerEbpf starts listening for DNS events.
97117
func ListenerEbpf(ebpfModPath string) error {
118+
probesAttached := 0
98119
m, err := core.LoadEbpfModule("opensnitch-dns.o", ebpfModPath)
99120
if err != nil {
100-
log.Warning("[eBPF DNS]: %s", err)
101121
return err
102122
}
103123
defer m.Close()
104124

125+
ebpfMod := dnsDefsT{}
126+
if err := m.Assign(&ebpfMod); err != nil {
127+
return err
128+
}
129+
130+
// --------------
131+
105132
// libbcc resolves the offsets for us. without bcc the offset for uprobes must parsed from the elf files
106133
// some how 0 must be replaced with the offset of getaddrinfo bcc does this using bcc_resolve_symname
107134

108135
// Attaching to uprobe using perf open might be a better aproach requires https://github.com/iovisor/gobpf/pull/277
109-
libcFile, err := findLibc()
110136

137+
libcFile, err := findLibc()
111138
if err != nil {
112-
log.Error("[eBPF DNS]: Failed to find libc.so: %v", err)
139+
log.Error("[eBPF DNS] Failed to find libc.so: %v", err)
113140
return err
114141
}
142+
ex, err := link.OpenExecutable(libcFile)
143+
if err != nil {
144+
return err
145+
}
146+
147+
// --------------
115148

116-
libcElf, err := elf.Open(libcFile)
149+
// User space needs to call perf_event_open() (...) before eBPF program can send data into it.
150+
rd, err := ringbuf.NewReader(ebpfMod.PerfEvents)
117151
if err != nil {
118-
log.Error("[eBPF DNS]: Failed to open %s: %v", libcFile, err)
119152
return err
120153
}
121-
probesAttached := 0
122-
for uprobe := range m.IterUprobes() {
123-
probeFunction := strings.Replace(uprobe.Name, "uretprobe/", "", 1)
124-
probeFunction = strings.Replace(probeFunction, "uprobe/", "", 1)
125-
offset, err := lookupSymbol(libcElf, probeFunction)
126-
if err != nil {
127-
log.Warning("[eBPF DNS]: Failed to find symbol for uprobe %s (offset: %d): %s\n", uprobe.Name, offset, err)
128-
continue
129-
}
130-
err = bpf.AttachUprobe(uprobe, libcFile, offset)
131-
if err != nil {
132-
log.Warning("[eBPF DNS]: Failed to attach uprobe %s : %s, (%s, %d)\n", uprobe.Name, err, libcFile, offset)
133-
continue
134-
}
135-
probesAttached++
154+
defer rd.Close()
155+
156+
// --------------
157+
158+
urg, err := ex.Uretprobe("gethostbyname", ebpfMod.URProbeGethostByname, nil)
159+
if err != nil {
160+
log.Error("[eBPF DNS] uretprobe__gethostbyname: %s", err)
161+
}
162+
defer urg.Close()
163+
probesAttached++
164+
165+
up, err := ex.Uprobe("getaddrinfo", ebpfMod.UProbeGetAddrinfo, nil)
166+
if err != nil {
167+
log.Error("[eBPF DNS] uprobe__getaddrinfo: %s", err)
168+
}
169+
defer up.Close()
170+
probesAttached++
171+
172+
urp, err := ex.Uretprobe("getaddrinfo", ebpfMod.URProbeGetAddrinfo, nil)
173+
if err != nil {
174+
log.Error("[eBPF-DNS] uretprobe__getaddrinfo: %s", err)
136175
}
176+
defer urp.Close()
177+
probesAttached++
137178

138179
if probesAttached == 0 {
139180
log.Warning("[eBPF DNS]: Failed to find symbols for uprobes.")
140181
return errors.New("Failed to find symbols for uprobes")
141182
}
142183

143-
// Reading Events
144-
channel := make(chan []byte)
145-
//log.Warning("EBPF-DNS: %+v\n", m)
146-
perfMap, err := bpf.InitPerfMap(m, "events", channel, nil)
147-
if err != nil {
148-
log.Error("[eBPF DNS]: Failed to init perf map: %s\n", err)
149-
return err
184+
// --------------
185+
186+
exitChannel := make(chan struct{})
187+
perfChan := make(chan []byte, 0)
188+
189+
for i := 0; i < runtime.NumCPU(); i++ {
190+
go spawnDNSWorker(i, perfChan, exitChannel)
150191
}
192+
193+
go func(perfChan chan []byte, rd *ringbuf.Reader) {
194+
for {
195+
select {
196+
case <-exitChannel:
197+
goto Exit
198+
default:
199+
record, err := rd.Read()
200+
if err != nil {
201+
if errors.Is(err, ringbuf.ErrClosed) {
202+
goto Exit
203+
}
204+
log.Debug("[eBPF DNS] reader error: %s", err)
205+
continue
206+
}
207+
perfChan <- record.RawSample
208+
}
209+
}
210+
Exit:
211+
log.Debug("[eBPF DNS] reader closed")
212+
}(perfChan, rd)
213+
151214
sig := make(chan os.Signal, 1)
152-
exitChannel := make(chan bool)
153215
signal.Notify(sig,
154216
syscall.SIGHUP,
155217
syscall.SIGINT,
156218
syscall.SIGTERM,
157219
syscall.SIGKILL,
158220
syscall.SIGQUIT)
159221

160-
for i := 0; i < 5; i++ {
161-
go spawnDNSWorker(i, channel, exitChannel)
162-
}
163-
164-
perfMap.PollStart()
165222
<-sig
166223
log.Info("[eBPF DNS]: Received signal: terminating ebpf dns hook.")
167-
perfMap.PollStop()
168-
for i := 0; i < 5; i++ {
169-
exitChannel <- true
224+
exitChannel <- struct{}{}
225+
for i := 0; i < runtime.NumCPU(); i++ {
226+
exitChannel <- struct{}{}
170227
}
171228
return nil
172229
}
173230

174-
func spawnDNSWorker(id int, channel chan []byte, exitChannel chan bool) {
231+
func spawnDNSWorker(id int, channel chan []byte, exitChannel chan struct{}) {
175232

176233
log.Debug("[eBPF DNS] worker initialized #%d", id)
177234
var event nameLookupEvent

daemon/go.mod

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,20 @@ module github.com/evilsocket/opensnitch/daemon
22

33
go 1.21
44

5-
//toolchain go1.23.6
5+
//toolchain go1.23.1
66

77
require (
8+
github.com/cilium/ebpf v0.16.0
89
github.com/fsnotify/fsnotify v1.4.7
910
github.com/golang/protobuf v1.5.0
1011
github.com/google/gopacket v1.1.19
1112
github.com/google/nftables v0.2.0
1213
github.com/google/uuid v1.3.0
13-
github.com/iovisor/gobpf v0.2.0
1414
github.com/varlink/go v0.4.0
1515
github.com/vishvananda/netlink v1.3.0
1616
github.com/vishvananda/netns v0.0.4
17-
golang.org/x/net v0.22.0
18-
golang.org/x/sys v0.18.0
17+
golang.org/x/net v0.23.0
18+
golang.org/x/sys v0.20.0
1919
google.golang.org/grpc v1.32.0
2020
google.golang.org/protobuf v1.26.0
2121
)
@@ -25,6 +25,7 @@ require (
2525
github.com/josharian/native v1.1.0 // indirect
2626
github.com/mdlayher/netlink v1.7.2 // indirect
2727
github.com/mdlayher/socket v0.5.0 // indirect
28+
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect
2829
golang.org/x/sync v0.6.0 // indirect
2930
golang.org/x/text v0.14.0 // indirect
3031
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect

daemon/go.sum

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
22
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
33
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
4+
github.com/cilium/ebpf v0.16.0 h1:+BiEnHL6Z7lXnlGUsXQPPAE7+kenAd4ES8MQ5min0Ok=
5+
github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE=
46
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
57
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
68
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
79
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
810
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
911
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
1012
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
13+
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
14+
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
1115
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
1216
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
1317
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
@@ -26,15 +30,21 @@ github.com/google/nftables v0.2.0 h1:PbJwaBmbVLzpeldoeUKGkE2RjstrjPKMl6oLrfEJ6/8
2630
github.com/google/nftables v0.2.0/go.mod h1:Beg6V6zZ3oEn0JuiUQ4wqwuyqqzasOltcoXPtgLbFp4=
2731
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
2832
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
29-
github.com/iovisor/gobpf v0.2.0 h1:34xkQxft+35GagXBk3n23eqhm0v7q0ejeVirb8sqEOQ=
30-
github.com/iovisor/gobpf v0.2.0/go.mod h1:WSY9Jj5RhdgC3ci1QaacvbFdQ8cbrEjrpiZbLHLt2s4=
3133
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
3234
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
35+
github.com/jsimonetti/rtnetlink/v2 v2.0.1 h1:xda7qaHDSVOsADNouv7ukSuicKZO7GgVUCXxpaIEIlM=
36+
github.com/jsimonetti/rtnetlink/v2 v2.0.1/go.mod h1:7MoNYNbb3UaDHtF8udiJo/RH6VsTKP1pqKLUTVCvToE=
37+
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
38+
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
39+
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
40+
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
3341
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
3442
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
3543
github.com/mdlayher/socket v0.5.0 h1:ilICZmJcQz70vrWVes1MFera4jGiWNocSkykwwoy3XI=
3644
github.com/mdlayher/socket v0.5.0/go.mod h1:WkcBFfvyG8QENs5+hfQPl1X6Jpd2yeLIYgrGFmJiJxI=
3745
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
46+
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
47+
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
3848
github.com/varlink/go v0.4.0 h1:+/BQoUO9eJK/+MTSHwFcJch7TMsb6N6Dqp6g0qaXXRo=
3949
github.com/varlink/go v0.4.0/go.mod h1:DKg9Y2ctoNkesREGAEak58l+jOC6JU2aqZvUYs5DynU=
4050
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
@@ -44,6 +54,8 @@ github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZla
4454
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
4555
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
4656
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
57+
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI=
58+
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
4759
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
4860
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
4961
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
@@ -55,8 +67,8 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r
5567
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
5668
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
5769
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
58-
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
59-
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
70+
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
71+
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
6072
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
6173
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
6274
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -68,8 +80,8 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
6880
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
6981
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
7082
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
71-
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
72-
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
83+
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
84+
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
7385
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
7486
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
7587
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=

0 commit comments

Comments
 (0)