Skip to content

Commit 045aa1d

Browse files
authored
Merge pull request #139 from digitalocean/anitgandhi/no-unsafe
ovsh: replace usage of unsafe package with binary marshaling and unmarshaling
2 parents b3b96b0 + ba1eb37 commit 045aa1d

File tree

4 files changed

+94
-57
lines changed

4 files changed

+94
-57
lines changed

ovsnl/client.go

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,20 +18,11 @@ import (
1818
"fmt"
1919
"os"
2020
"strings"
21-
"unsafe"
2221

2322
"github.com/digitalocean/go-openvswitch/ovsnl/internal/ovsh"
2423
"github.com/mdlayher/genetlink"
2524
)
2625

27-
// Sizes of various structures, used in unsafe casts.
28-
const (
29-
sizeofHeader = int(unsafe.Sizeof(ovsh.Header{}))
30-
31-
sizeofDPStats = int(unsafe.Sizeof(ovsh.DPStats{}))
32-
sizeofDPMegaflowStats = int(unsafe.Sizeof(ovsh.DPMegaflowStats{}))
33-
)
34-
3526
// A Client is a Linux Open vSwitch generic netlink client.
3627
type Client struct {
3728
// Datapath provides access to DatapathService methods.
@@ -116,20 +107,3 @@ func (c *Client) initFamily(f genetlink.Family) error {
116107
return fmt.Errorf("unknown OVS generic netlink family: %q", f.Name)
117108
}
118109
}
119-
120-
// headerBytes converts an ovsh.Header into a byte slice.
121-
func headerBytes(h ovsh.Header) []byte {
122-
b := *(*[sizeofHeader]byte)(unsafe.Pointer(&h)) // #nosec G103
123-
return b[:]
124-
}
125-
126-
// parseHeader converts a byte slice into ovsh.Header.
127-
func parseHeader(b []byte) (ovsh.Header, error) {
128-
// Verify that the byte slice is long enough before doing unsafe casts.
129-
if l := len(b); l < sizeofHeader {
130-
return ovsh.Header{}, fmt.Errorf("not enough data for OVS message header: %d bytes", l)
131-
}
132-
133-
h := *(*ovsh.Header)(unsafe.Pointer(&b[:sizeofHeader][0])) // #nosec G103
134-
return h, nil
135-
}

ovsnl/datapath.go

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
package ovsnl
1616

1717
import (
18-
"fmt"
19-
"unsafe"
18+
"encoding/binary"
2019

2120
"github.com/digitalocean/go-openvswitch/ovsnl/internal/ovsh"
2221
"github.com/mdlayher/genetlink"
@@ -98,15 +97,20 @@ type DatapathMegaflowStats struct {
9897

9998
// List lists all Datapaths in the kernel.
10099
func (s *DatapathService) List() ([]Datapath, error) {
100+
data, err := ovsh.MarshalBinary(&ovsh.Header{
101+
// Query all datapaths.
102+
Ifindex: 0,
103+
})
104+
if err != nil {
105+
return nil, err
106+
}
107+
101108
req := genetlink.Message{
102109
Header: genetlink.Header{
103110
Command: ovsh.DpCmdGet,
104111
Version: uint8(s.f.Version),
105112
},
106-
// Query all datapaths.
107-
Data: headerBytes(ovsh.Header{
108-
Ifindex: 0,
109-
}),
113+
Data: data,
110114
}
111115

112116
flags := netlink.Request | netlink.Dump
@@ -134,8 +138,9 @@ func parseDatapaths(msgs []genetlink.Message) ([]Datapath, error) {
134138
Index: int(h.Ifindex),
135139
}
136140

137-
// Skip the header to parse attributes.
138-
attrs, err := netlink.UnmarshalAttributes(m.Data[sizeofHeader:])
141+
// Skip past the header to parse attributes.
142+
hdrSize := binary.Size(h)
143+
attrs, err := netlink.UnmarshalAttributes(m.Data[hdrSize:])
139144
if err != nil {
140145
return nil, err
141146
}
@@ -167,13 +172,12 @@ func parseDatapaths(msgs []genetlink.Message) ([]Datapath, error) {
167172

168173
// parseDPStats converts a byte slice into DatapathStats.
169174
func parseDPStats(b []byte) (DatapathStats, error) {
170-
// Verify that the byte slice is the correct length before doing
171-
// unsafe casts.
172-
if want, got := sizeofDPStats, len(b); want != got {
173-
return DatapathStats{}, fmt.Errorf("unexpected datapath stats structure size, want %d, got %d", want, got)
175+
s := new(ovsh.DPStats)
176+
err := ovsh.UnmarshalBinary(b, s)
177+
if err != nil {
178+
return DatapathStats{}, err
174179
}
175180

176-
s := *(*ovsh.DPStats)(unsafe.Pointer(&b[0])) // #nosec G103
177181
return DatapathStats{
178182
Hit: s.Hit,
179183
Missed: s.Missed,
@@ -184,16 +188,24 @@ func parseDPStats(b []byte) (DatapathStats, error) {
184188

185189
// parseDPMegaflowStats converts a byte slice into DatapathMegaflowStats.
186190
func parseDPMegaflowStats(b []byte) (DatapathMegaflowStats, error) {
187-
// Verify that the byte slice is the correct length before doing
188-
// unsafe casts.
189-
if want, got := sizeofDPMegaflowStats, len(b); want != got {
190-
return DatapathMegaflowStats{}, fmt.Errorf("unexpected datapath megaflow stats structure size, want %d, got %d", want, got)
191+
s := new(ovsh.DPMegaflowStats)
192+
err := ovsh.UnmarshalBinary(b, s)
193+
if err != nil {
194+
return DatapathMegaflowStats{}, err
191195
}
192196

193-
s := *(*ovsh.DPMegaflowStats)(unsafe.Pointer(&b[0])) // #nosec G103
194-
195197
return DatapathMegaflowStats{
196198
MaskHits: s.Mask_hit,
197199
Masks: s.Masks,
198200
}, nil
199201
}
202+
203+
// parseHeader converts a byte slice into ovsh.Header.
204+
func parseHeader(b []byte) (ovsh.Header, error) {
205+
h := new(ovsh.Header)
206+
err := ovsh.UnmarshalBinary(b, h)
207+
if err != nil {
208+
return ovsh.Header{}, err
209+
}
210+
return *h, nil
211+
}

ovsnl/datapath_linux_test.go

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
// limitations under the License.
1414

1515
//go:build linux
16+
// +build linux
1617

1718
package ovsnl
1819

1920
import (
2021
"testing"
21-
"unsafe"
2222

2323
"github.com/digitalocean/go-openvswitch/ovsnl/internal/ovsh"
2424
"github.com/google/go-cmp/cmp"
@@ -136,7 +136,8 @@ func TestClientDatapathListOK(t *testing.T) {
136136
t.Fatalf("unexpected generic netlink command (-want +got):\n%s", diff)
137137
}
138138

139-
h, err := parseHeader(greq.Data)
139+
h := new(ovsh.Header)
140+
err := ovsh.UnmarshalBinary(greq.Data, h)
140141
if err != nil {
141142
t.Fatalf("failed to parse OvS generic netlink header: %v", err)
142143
}
@@ -147,7 +148,7 @@ func TestClientDatapathListOK(t *testing.T) {
147148

148149
return []genetlink.Message{
149150
{
150-
Data: mustMarshalDatapath(system),
151+
Data: mustMarshalDatapath(t, system),
151152
},
152153
}, nil
153154
}))
@@ -172,29 +173,34 @@ func TestClientDatapathListOK(t *testing.T) {
172173
}
173174
}
174175

175-
func mustMarshalDatapath(dp Datapath) []byte {
176-
h := ovsh.Header{
176+
func mustMarshalDatapath(t *testing.T, dp Datapath) []byte {
177+
hb, err := ovsh.MarshalBinary(&ovsh.Header{
177178
Ifindex: int32(dp.Index),
179+
})
180+
if err != nil {
181+
t.Fatal(t)
178182
}
179183

180-
hb := headerBytes(h)
181-
182-
s := ovsh.DPStats{
184+
sb, err := ovsh.MarshalBinary(&ovsh.DPStats{
183185
Hit: dp.Stats.Hit,
184186
Missed: dp.Stats.Missed,
185187
Lost: dp.Stats.Lost,
186188
Flows: dp.Stats.Flows,
189+
})
190+
if err != nil {
191+
t.Fatal(t)
187192
}
188193

189-
sb := *(*[sizeofDPStats]byte)(unsafe.Pointer(&s)) // #nosec G103
190-
191194
ms := ovsh.DPMegaflowStats{
192195
Mask_hit: dp.MegaflowStats.MaskHits,
193196
Masks: dp.MegaflowStats.Masks,
194197
// Pad already set to zero.
195198
}
196199

197-
msb := *(*[sizeofDPMegaflowStats]byte)(unsafe.Pointer(&ms)) // #nosec G103
200+
msb, err := ovsh.MarshalBinary(&ms)
201+
if err != nil {
202+
t.Fatal(t)
203+
}
198204

199205
ab := mustMarshalAttributes([]netlink.Attribute{
200206
{
@@ -207,7 +213,7 @@ func mustMarshalDatapath(dp Datapath) []byte {
207213
},
208214
{
209215
Type: ovsh.DpAttrStats,
210-
Data: sb[:],
216+
Data: sb,
211217
},
212218
{
213219
Type: ovsh.DpAttrMegaflowStats,

ovsnl/internal/ovsh/binary.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright 2017 DigitalOcean.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package ovsh
16+
17+
import (
18+
"bytes"
19+
"encoding/binary"
20+
"fmt"
21+
)
22+
23+
// ovsTypes defines a type set of the types defined in struct.go
24+
type ovsTypes interface {
25+
Header | DPStats | DPMegaflowStats | VportStats | FlowStats
26+
}
27+
28+
// MarshalBinary is a generic binary marshaling function for the type defined in struct.go
29+
func MarshalBinary[T ovsTypes](data *T) ([]byte, error) {
30+
var buf bytes.Buffer
31+
err := binary.Write(&buf, binary.NativeEndian, data)
32+
return buf.Bytes(), err
33+
}
34+
35+
// UnmarshalBinary is a generic binary unmarshaling function for the type defined in struct.go
36+
func UnmarshalBinary[T ovsTypes](data []byte, dst *T) error {
37+
// Verify that the byte slice has enough data before unmarshaling.
38+
if want, got := binary.Size(*dst), len(data); got < want {
39+
return fmt.Errorf("unexpected size of struct %T, want at least %d, got %d", *dst, want, got)
40+
}
41+
42+
*dst = *new(T) // reset the contents, just to be safe
43+
buf := bytes.NewBuffer(data)
44+
return binary.Read(buf, binary.NativeEndian, dst)
45+
}

0 commit comments

Comments
 (0)