Skip to content

Commit 7f3926c

Browse files
authored
Merge pull request #3 from deeglaze/fix_ioctl
Fix Linux ABI
2 parents 006fea8 + 31cf90d commit 7f3926c

File tree

4 files changed

+190
-80
lines changed

4 files changed

+190
-80
lines changed

client/client_linux.go

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,13 @@ func (d *LinuxDevice) Close() error {
8585
// Ioctl sends a command with its wrapped request and response values to the Linux device.
8686
func (d *LinuxDevice) Ioctl(command uintptr, req interface{}) (uintptr, error) {
8787
switch sreq := req.(type) {
88-
case *labi.SnpUserGuestRequestSafe:
88+
case *labi.SnpUserGuestRequest:
8989
return labi.Ioctl(d.fd, command, sreq)
9090
}
9191
return 0, fmt.Errorf("unexpected request value: %v", req)
9292
}
9393

94-
func message(d Device, command uintptr, req *labi.SnpUserGuestRequestSafe) error {
94+
func message(d Device, command uintptr, req *labi.SnpUserGuestRequest) error {
9595
result, err := d.Ioctl(command, req)
9696
if err != nil {
9797
return err
@@ -108,13 +108,12 @@ func message(d Device, command uintptr, req *labi.SnpUserGuestRequestSafe) error
108108
// GetRawReportAtVmpl requests for an attestation report at the given VMPL that incorporates the
109109
// given user data.
110110
func GetRawReportAtVmpl(d Device, userData [64]byte, vmpl int) ([]byte, error) {
111-
var snpReportRsp labi.SnpReportResp
112-
snpReportReq := labi.SnpReportReq{
113-
UserData: userData,
114-
Vmpl: uint32(vmpl),
115-
}
116-
userGuestReq := labi.SnpUserGuestRequestSafe{
117-
ReqData: &snpReportReq,
111+
var snpReportRsp labi.SnpReportRespABI
112+
userGuestReq := labi.SnpUserGuestRequest{
113+
ReqData: &labi.SnpReportReqABI{
114+
UserData: userData,
115+
Vmpl: uint32(vmpl),
116+
},
118117
RespData: &snpReportRsp,
119118
}
120119
if err := message(d, labi.IocSnpGetReport, &userGuestReq); err != nil {
@@ -148,16 +147,16 @@ func GetReport(d Device, userData [64]byte) (*pb.Report, error) {
148147
// returns the signed attestation report containing userData and the certificate chain for the
149148
// report's endorsement key.
150149
func getExtendedReportIn(d Device, userData [64]byte, vmpl int, certs []byte) ([]byte, uint32, error) {
151-
var snpReportRsp labi.SnpReportResp
152-
snpExtReportReq := labi.SnpExtendedReportReqSafe{
153-
Data: labi.SnpReportReq{
150+
var snpReportRsp labi.SnpReportRespABI
151+
snpExtReportReq := labi.SnpExtendedReportReq{
152+
Data: labi.SnpReportReqABI{
154153
UserData: userData,
155154
Vmpl: uint32(vmpl),
156155
},
157156
Certs: certs,
158157
CertsLength: uint32(len(certs)),
159158
}
160-
userGuestReq := labi.SnpUserGuestRequestSafe{
159+
userGuestReq := labi.SnpUserGuestRequest{
161160
ReqData: &snpExtReportReq,
162161
RespData: &snpReportRsp,
163162
}

client/linuxabi/linux_abi.go

Lines changed: 164 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616
package linuxabi
1717

1818
import (
19+
"errors"
20+
"fmt"
21+
"reflect"
1922
"unsafe"
2023

21-
"github.com/google/go-sev-guest/abi"
2224
"golang.org/x/sys/unix"
2325
)
2426

@@ -34,7 +36,7 @@ const (
3436
iocNrshift = 0
3537
iocTypeshift = (iocNrshift + iocNrbits)
3638
iocSizeshift = (iocTypeshift + iocTypebits)
37-
iocDirshift = (iocSizeshift + iocDirbits)
39+
iocDirshift = (iocSizeshift + iocSizebits)
3840
iocWrite = 1
3941
iocRead = 2
4042

@@ -43,14 +45,22 @@ const (
4345
iocSnpWithoutNr = ((iocWrite | iocRead) << iocDirshift) |
4446
(iocTypeSnpGuestReq << iocTypeshift) |
4547
// unsafe.Sizeof(snpUserGuestRequest)
46-
(24 << iocSizeshift)
48+
(32 << iocSizeshift)
4749

4850
// IocSnpGetReport is the ioctl command for getting an attestation report
4951
IocSnpGetReport = iocSnpWithoutNr | (0x0 << iocNrshift)
5052

5153
// IocSnpGetReport is the ioctl command for getting an extended attestation report that includes
5254
// certificate information.
5355
IocSnpGetExtendedReport = iocSnpWithoutNr | (0x2 << iocNrshift)
56+
57+
// The message version for MSG_REPORT_REQ in the SNP API. Specified as 1.
58+
guestMsgVersion = 1
59+
60+
// These numbers are from the uapi header sev_guest.h
61+
snpResportRespSize = 4000
62+
msgReportReqHeaderSize = 0x20
63+
SnpReportRespReportSize = snpResportRespSize - msgReportReqHeaderSize
5464
)
5565

5666
const (
@@ -93,9 +103,9 @@ func (err SevEsErr) Error() string {
93103
return "unknown error"
94104
}
95105

96-
// SnpReportReq is Linux's sev-guest ioctl abi for sending a GET_REPORT request. See
106+
// SnpReportReqABI is Linux's sev-guest ioctl abi for sending a GET_REPORT request. See
97107
// include/uapi/linux/sev-guest.h
98-
type SnpReportReq struct {
108+
type SnpReportReqABI struct {
99109
// UserData to be included in the report
100110
UserData [64]uint8
101111

@@ -106,87 +116,187 @@ type SnpReportReq struct {
106116
reserved [28]byte
107117
}
108118

109-
// SnpReportResp is Linux's sev-guest ioctl abi for receiving a GET_REPORT response.
110-
type SnpReportResp struct {
119+
// SnpReportRespABI is Linux's sev-guest ioctl abi for receiving a GET_REPORT response.
120+
// The size is expected to be snpReportRespSize.
121+
type SnpReportRespABI struct {
122+
Status uint32
123+
ReportSize uint32
124+
reserved [0x20 - 8]byte
111125
// Data is the response data, see SEV-SNP spec for the format
112-
Data [abi.ReportSize]uint8
126+
Data [SnpReportRespReportSize]uint8
113127
}
114128

115-
// SnpExtendedReportReqSafe is close to Linux's sev-guest ioctl abi for sending a GET_EXTENDED_REPORT request,
116-
// but uses safer types for the Ioctl interface.
117-
type SnpExtendedReportReqSafe struct {
118-
Data SnpReportReq
129+
// ABI returns the same object since it doesn't need a separate representation across the interface.
130+
func (r *SnpReportReqABI) ABI() BinaryConversion { return r }
131+
132+
// Pointer returns a pointer to the object itself.
133+
func (r *SnpReportReqABI) Pointer() unsafe.Pointer {
134+
return unsafe.Pointer(r)
135+
}
136+
137+
// Finish is a no-op.
138+
func (r *SnpReportReqABI) Finish(b BinaryConvertible) error { return nil }
139+
140+
// ABI returns the same object since it doesn't need a separate representation across the interface.
141+
func (r *SnpReportRespABI) ABI() BinaryConversion { return r }
142+
143+
// Pointer returns a pointer to the object itself.
144+
func (r *SnpReportRespABI) Pointer() unsafe.Pointer {
145+
return unsafe.Pointer(r)
146+
}
147+
148+
// Finish checks the status of the message and translates it to a Golang error.
149+
func (r *SnpReportRespABI) Finish(b BinaryConvertible) error {
150+
if r.Status != 0 {
151+
switch r.Status {
152+
case 0x16: // Value from MSG_REPORT_RSP specification in SNP API.
153+
return errors.New("get_report had invalid parameters")
154+
default:
155+
return fmt.Errorf("unknown status: 0x%x", r.Status)
156+
}
157+
}
158+
return nil
159+
}
160+
161+
// SnpExtendedReportReqABI is Linux's sev-guest ioctl abi for sending a GET_EXTENDED_REPORT request.
162+
type SnpExtendedReportReqABI struct {
163+
Data SnpReportReqABI
119164

120165
// Where to copy the certificate blob.
121-
Certs []byte
166+
CertsAddress unsafe.Pointer
122167

123168
// length of the certificate blob
124169
CertsLength uint32
125170
}
126171

127-
// SnpExtendedReportReq is Linux's sev-guest ioctl abi for sending a GET_EXTENDED_REPORT request.
172+
// SnpExtendedReportReq is close to Linux's sev-guest ioctl abi for sending a GET_EXTENDED_REPORT request,
173+
// but uses safer types for the Ioctl interface.
128174
type SnpExtendedReportReq struct {
129-
Data SnpReportReq
175+
Data SnpReportReqABI
130176

131-
// Where to copy the certificate blob.
132-
CertsAddress uint64
177+
// Certs receives the certificate blob after the extended report request.
178+
Certs []byte
133179

134-
// length of the certificate blob
180+
// CertsLength is the length of the certificate blob.
135181
CertsLength uint32
136182
}
137183

138-
// SnpUserGuestRequest is Linux's sev-guest ioctl abi for issuing a guest message.
139-
type SnpUserGuestRequest struct {
184+
// Pointer returns a pointer so the object itself.
185+
func (r *SnpExtendedReportReqABI) Pointer() unsafe.Pointer {
186+
return unsafe.Pointer(r)
187+
}
188+
189+
// Finish writes back the changed CertsLength value.
190+
func (r *SnpExtendedReportReqABI) Finish(b BinaryConvertible) error {
191+
s, ok := b.(*SnpExtendedReportReq)
192+
if !ok {
193+
return fmt.Errorf("Finish argument is %v. Expects a *SnpExtendedReportReq", reflect.TypeOf(b))
194+
}
195+
s.CertsLength = r.CertsLength
196+
return nil
197+
}
198+
199+
// ABI returns an object that can cross the ABI boundary and copy back changes to the original
200+
// object.
201+
func (r *SnpExtendedReportReq) ABI() BinaryConversion {
202+
var certsAddress unsafe.Pointer
203+
if len(r.Certs) != 0 {
204+
certsAddress = unsafe.Pointer(&r.Certs[0])
205+
}
206+
return &SnpExtendedReportReqABI{
207+
Data: r.Data,
208+
CertsAddress: certsAddress,
209+
CertsLength: r.CertsLength,
210+
}
211+
}
212+
213+
// SnpUserGuestRequestABI is Linux's sev-guest ioctl abi for issuing a guest message.
214+
type SnpUserGuestRequestABI struct {
215+
GuestMsgVersion uint32
140216
// Request and response structure address.
141-
ReqData uint64
142-
RespData uint64
217+
ReqData unsafe.Pointer
218+
RespData unsafe.Pointer
143219
// firmware error code on failure (see psp-sev.h in Linux kernel)
144220
FwErr uint64
145221
}
146222

147-
// SnpUserGuestRequestSafe is Linux's sev-guest ioctl interface for issuing a guest message. The
223+
type snpUserGuestRequestConversion struct {
224+
abi SnpUserGuestRequestABI
225+
reqConv BinaryConversion
226+
respConv BinaryConversion
227+
}
228+
229+
// SnpUserGuestRequest is Linux's sev-guest ioctl interface for issuing a guest message. The
148230
// types here enhance runtime safety when using Ioctl as an interface.
149-
type SnpUserGuestRequestSafe struct {
231+
type SnpUserGuestRequest struct {
150232
// Request and response structure address.
151-
ReqData interface{}
152-
RespData interface{}
233+
ReqData BinaryConvertible
234+
RespData BinaryConvertible
153235
// firmware error code on failure (see psp-sev.h in Linux kernel)
154236
FwErr uint64
155237
}
156238

239+
// ABI returns an object that can cross the ABI boundary and copy back changes to the original
240+
// object.
241+
func (r *SnpUserGuestRequest) ABI() BinaryConversion {
242+
result := &snpUserGuestRequestConversion{
243+
reqConv: r.ReqData.ABI(),
244+
respConv: r.RespData.ABI(),
245+
}
246+
result.abi.GuestMsgVersion = guestMsgVersion
247+
result.abi.ReqData = result.reqConv.Pointer()
248+
result.abi.RespData = result.respConv.Pointer()
249+
return result
250+
}
251+
252+
// Pointer returns a pointer to the object that crosses the ABI boundary.
253+
func (r *snpUserGuestRequestConversion) Pointer() unsafe.Pointer {
254+
return unsafe.Pointer(&r.abi)
255+
}
256+
257+
// Finish writes back the FwErr and any changes to the request or response objects.
258+
func (r *snpUserGuestRequestConversion) Finish(b BinaryConvertible) error {
259+
s, ok := b.(*SnpUserGuestRequest)
260+
if !ok {
261+
return fmt.Errorf("Finish argument is %v. Expects a *SnpUserGuestRequestSafe", reflect.TypeOf(b))
262+
}
263+
if err := r.reqConv.Finish(s.ReqData); err != nil {
264+
return fmt.Errorf("could not finalize request data: %v", err)
265+
}
266+
if err := r.respConv.Finish(s.RespData); err != nil {
267+
return fmt.Errorf("could not finalize response data: %v", err)
268+
}
269+
s.FwErr = r.abi.FwErr
270+
return nil
271+
}
272+
273+
// BinaryConversion is an interface that abstracts a "stand-in" object that passes through an ABI
274+
// boundary and can finalize changes to the original object.
275+
type BinaryConversion interface {
276+
Pointer() unsafe.Pointer
277+
Finish(BinaryConvertible) error
278+
}
279+
280+
// BinaryConvertible is an interface for an object that can produce a partner BinaryConversion
281+
// object to allow its representation to pass the ABI boundary.
282+
type BinaryConvertible interface {
283+
ABI() BinaryConversion
284+
}
285+
157286
// Ioctl performs the ioctl Linux syscall with the sev-guest Linux ABI unsafe pointer
158287
// manipulation contained all in this call.
159-
func Ioctl(fd int, command uintptr, sreq *SnpUserGuestRequestSafe) (uintptr, error) {
160-
// Limit unsafe pointers to this scope by converting internal types to ABI types before a
161-
// raw ioctl call, and converting back.
162-
safeReqData := sreq.ReqData
163-
var reqData interface{}
164-
switch extReq := safeReqData.(type) {
165-
case *SnpExtendedReportReqSafe:
166-
var certsAddress uint64
167-
if len(extReq.Certs) > 0 {
168-
certsAddress = uint64(uintptr(unsafe.Pointer(&extReq.Certs[0])))
169-
}
170-
reqData = &SnpExtendedReportReq{
171-
Data: extReq.Data,
172-
CertsAddress: certsAddress,
173-
CertsLength: extReq.CertsLength,
174-
}
175-
}
176-
abi := SnpUserGuestRequest{
177-
ReqData: uint64(uintptr(unsafe.Pointer(&reqData))),
178-
RespData: uint64(uintptr(unsafe.Pointer(&sreq.RespData))),
288+
func Ioctl(fd int, command uintptr, req *SnpUserGuestRequest) (uintptr, error) {
289+
abi := req.ABI()
290+
result, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(fd), command, uintptr(abi.Pointer()))
291+
abi.Finish(req)
292+
293+
// TODO(Issue #5): remove the work around for the kernel bug that writes
294+
// uninitialized memory back on non-EIO.
295+
if errno != unix.EIO {
296+
req.FwErr = 0
179297
}
180-
ptr := uintptr(unsafe.Pointer(&abi))
181-
result, _, errno := unix.RawSyscall(unix.SYS_IOCTL, uintptr(fd), command, ptr)
182298
if errno != 0 {
183299
return 0, errno
184300
}
185-
// Copy back the certsLength from the request copy if an extended report request.
186-
switch extReq := reqData.(type) {
187-
case *SnpExtendedReportReq:
188-
safeReqData.(*SnpExtendedReportReqSafe).CertsLength = extReq.CertsLength
189-
}
190-
sreq.FwErr = abi.FwErr
191301
return result, nil
192302
}

0 commit comments

Comments
 (0)