From a0613cead6e63e8100896dc492366c7667b29eef Mon Sep 17 00:00:00 2001 From: Al Cutter Date: Fri, 15 Mar 2024 17:39:57 +0000 Subject: [PATCH] Save applet crash logs (#216) --- api/api.go | 2 + api/api.pb.go | 178 ++++++++++++++++++++++++++++++----- api/api.proto | 15 +++ cmd/witnessctl/api.go | 56 ++++++++++- cmd/witnessctl/witnessctl.go | 11 +++ go.mod | 2 +- go.sum | 4 +- trusted_os/ctl.go | 58 +++++++++++- trusted_os/flash.go | 30 ++++++ trusted_os/load.go | 4 +- trusted_os/main.go | 7 +- trusted_os/usb_hid.go | 3 + 12 files changed, 335 insertions(+), 35 deletions(-) diff --git a/api/api.go b/api/api.go index e5545e8..8812f29 100644 --- a/api/api.go +++ b/api/api.go @@ -47,6 +47,8 @@ const ( U2FHID_ARMORY_HAB // Fetch latest debug/console logs U2FHID_ARMORY_CONSOLE_LOGS + // Fetch stored crash logs from most recent applet crash + U2FHID_ARMORY_CRASH_LOGS ) var emptyResponse []byte diff --git a/api/api.pb.go b/api/api.pb.go index 0391658..9849bd3 100644 --- a/api/api.pb.go +++ b/api/api.pb.go @@ -579,6 +579,108 @@ func (x *Configuration) GetNTPServer() string { return "" } +type LogMessagesRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Continue bool `protobuf:"varint,1,opt,name=Continue,proto3" json:"Continue,omitempty"` +} + +func (x *LogMessagesRequest) Reset() { + *x = LogMessagesRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[5] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogMessagesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogMessagesRequest) ProtoMessage() {} + +func (x *LogMessagesRequest) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[5] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogMessagesRequest.ProtoReflect.Descriptor instead. +func (*LogMessagesRequest) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{5} +} + +func (x *LogMessagesRequest) GetContinue() bool { + if x != nil { + return x.Continue + } + return false +} + +type LogMessagesResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Payload []byte `protobuf:"bytes,1,opt,name=Payload,proto3" json:"Payload,omitempty"` + More bool `protobuf:"varint,2,opt,name=More,proto3" json:"More,omitempty"` +} + +func (x *LogMessagesResponse) Reset() { + *x = LogMessagesResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_api_proto_msgTypes[6] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *LogMessagesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LogMessagesResponse) ProtoMessage() {} + +func (x *LogMessagesResponse) ProtoReflect() protoreflect.Message { + mi := &file_api_proto_msgTypes[6] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use LogMessagesResponse.ProtoReflect.Descriptor instead. +func (*LogMessagesResponse) Descriptor() ([]byte, []int) { + return file_api_proto_rawDescGZIP(), []int{6} +} + +func (x *LogMessagesResponse) GetPayload() []byte { + if x != nil { + return x.Payload + } + return nil +} + +func (x *LogMessagesResponse) GetMore() bool { + if x != nil { + return x.More + } + return false +} + type Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -591,7 +693,7 @@ type Response struct { func (x *Response) Reset() { *x = Response{} if protoimpl.UnsafeEnabled { - mi := &file_api_proto_msgTypes[5] + mi := &file_api_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -604,7 +706,7 @@ func (x *Response) String() string { func (*Response) ProtoMessage() {} func (x *Response) ProtoReflect() protoreflect.Message { - mi := &file_api_proto_msgTypes[5] + mi := &file_api_proto_msgTypes[7] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -617,7 +719,7 @@ func (x *Response) ProtoReflect() protoreflect.Message { // Deprecated: Use Response.ProtoReflect.Descriptor instead. func (*Response) Descriptor() ([]byte, []int) { - return file_api_proto_rawDescGZIP(), []int{5} + return file_api_proto_rawDescGZIP(), []int{7} } func (x *Response) GetError() ErrorCode { @@ -695,15 +797,23 @@ var file_api_proto_rawDesc = []byte{ 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x52, 0x65, 0x73, 0x6f, 0x6c, 0x76, 0x65, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x4e, 0x54, 0x50, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x4e, 0x54, 0x50, 0x53, 0x65, 0x72, - 0x76, 0x65, 0x72, 0x22, 0x4a, 0x0a, 0x08, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x24, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, - 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x05, - 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2a, - 0x28, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x08, 0x0a, 0x04, - 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x11, 0x0a, 0x0d, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x49, - 0x43, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x3b, - 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x76, 0x65, 0x72, 0x22, 0x30, 0x0a, 0x12, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6f, 0x6e, + 0x74, 0x69, 0x6e, 0x75, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x43, 0x6f, 0x6e, + 0x74, 0x69, 0x6e, 0x75, 0x65, 0x22, 0x43, 0x0a, 0x13, 0x4c, 0x6f, 0x67, 0x4d, 0x65, 0x73, 0x73, + 0x61, 0x67, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x4d, 0x6f, 0x72, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x04, 0x4d, 0x6f, 0x72, 0x65, 0x22, 0x4a, 0x0a, 0x08, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x24, 0x0a, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0e, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x45, 0x72, 0x72, 0x6f, + 0x72, 0x43, 0x6f, 0x64, 0x65, 0x52, 0x05, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x12, 0x18, 0x0a, 0x07, + 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x50, + 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x2a, 0x28, 0x0a, 0x09, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x43, + 0x6f, 0x64, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x4f, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x11, 0x0a, + 0x0d, 0x47, 0x45, 0x4e, 0x45, 0x52, 0x49, 0x43, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x01, + 0x42, 0x08, 0x5a, 0x06, 0x2e, 0x2f, 0x3b, 0x61, 0x70, 0x69, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( @@ -719,15 +829,17 @@ func file_api_proto_rawDescGZIP() []byte { } var file_api_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_api_proto_msgTypes = make([]protoimpl.MessageInfo, 6) +var file_api_proto_msgTypes = make([]protoimpl.MessageInfo, 8) var file_api_proto_goTypes = []interface{}{ - (ErrorCode)(0), // 0: api.ErrorCode - (*AppletUpdate)(nil), // 1: api.AppletUpdate - (*AppletUpdateHeader)(nil), // 2: api.AppletUpdateHeader - (*Status)(nil), // 3: api.Status - (*WitnessStatus)(nil), // 4: api.WitnessStatus - (*Configuration)(nil), // 5: api.Configuration - (*Response)(nil), // 6: api.Response + (ErrorCode)(0), // 0: api.ErrorCode + (*AppletUpdate)(nil), // 1: api.AppletUpdate + (*AppletUpdateHeader)(nil), // 2: api.AppletUpdateHeader + (*Status)(nil), // 3: api.Status + (*WitnessStatus)(nil), // 4: api.WitnessStatus + (*Configuration)(nil), // 5: api.Configuration + (*LogMessagesRequest)(nil), // 6: api.LogMessagesRequest + (*LogMessagesResponse)(nil), // 7: api.LogMessagesResponse + (*Response)(nil), // 8: api.Response } var file_api_proto_depIdxs = []int32{ 2, // 0: api.AppletUpdate.Header:type_name -> api.AppletUpdateHeader @@ -807,6 +919,30 @@ func file_api_proto_init() { } } file_api_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogMessagesRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*LogMessagesResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_api_proto_msgTypes[7].Exporter = func(v interface{}, i int) interface{} { switch v := v.(*Response); i { case 0: return &v.state @@ -829,7 +965,7 @@ func file_api_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_api_proto_rawDesc, NumEnums: 1, - NumMessages: 6, + NumMessages: 8, NumExtensions: 0, NumServices: 0, }, diff --git a/api/api.proto b/api/api.proto index 96b27d4..0473207 100644 --- a/api/api.proto +++ b/api/api.proto @@ -181,6 +181,21 @@ message Configuration { string NTPServer = 6; } +/* + +Log messages + +*/ + +message LogMessagesRequest { + bool Continue = 1; +} + +message LogMessagesResponse { + bytes Payload = 1; + bool More = 2; +} + message Response { ErrorCode Error = 1; bytes Payload = 2; diff --git a/cmd/witnessctl/api.go b/cmd/witnessctl/api.go index e678531..5e92c56 100644 --- a/cmd/witnessctl/api.go +++ b/cmd/witnessctl/api.go @@ -18,8 +18,10 @@ package main import ( + "compress/gzip" "errors" "fmt" + "io" "log" "net" "os" @@ -78,13 +80,61 @@ func (d Device) hab() error { return nil } -func (d Device) consoleLogs() (string, error) { - buf, err := d.u2f.Command(api.U2FHID_ARMORY_CONSOLE_LOGS, nil) +func (d Device) getLogMessages(cmd byte) (string, error) { + r, w := io.Pipe() + defer r.Close() + + errC := make(chan error, 1) + // Kick off a goroutine to fetch chunks of log and pipe it into the + // decompressor. + go func() { + // Signal that there's no more compressed data. + defer w.Close() + defer close(errC) + + req := &api.LogMessagesRequest{} + rsp := &api.LogMessagesResponse{More: true} + for rsp.More { + rb, _ := proto.Marshal(req) + buf, err := d.u2f.Command(cmd, rb) + if err != nil { + errC <- err + return + } + if err := proto.Unmarshal(buf, rsp); err != nil { + errC <- err + return + } + w.Write(rsp.GetPayload()) + req.Continue = true + + // Don't overload the HID endpoint + time.Sleep(10 * time.Millisecond) + } + }() + + gz, err := gzip.NewReader(r) + if err != nil { + log.Printf("Failed to create gzip reader: %v", err) + return "", err + } + gz.Close() + + // Grab the decompressed logs, and return + s, err := io.ReadAll(gz) if err != nil { return "", err } - return string(buf), nil + return string(s), <-errC +} + +func (d Device) consoleLogs() (string, error) { + return d.getLogMessages(api.U2FHID_ARMORY_CONSOLE_LOGS) +} + +func (d Device) crashLogs() (string, error) { + return d.getLogMessages(api.U2FHID_ARMORY_CRASH_LOGS) } func (d Device) sendUpdateHeader(signature []byte, total int) (err error) { diff --git a/cmd/witnessctl/witnessctl.go b/cmd/witnessctl/witnessctl.go index 83b2010..7da3730 100644 --- a/cmd/witnessctl/witnessctl.go +++ b/cmd/witnessctl/witnessctl.go @@ -57,6 +57,7 @@ type Config struct { status bool consoleLogs bool + crashLogs bool hab bool otaELF string @@ -81,6 +82,7 @@ func init() { flag.StringVar(&conf.hidPath, "d", "", "HID path of witness device to act upon (use -s to list devices)") flag.BoolVar(&conf.status, "s", false, "get witness status") flag.BoolVar(&conf.consoleLogs, "l", false, "get witness console/debug logs") + flag.BoolVar(&conf.crashLogs, "L", false, "get crash logs from most recent witness failure") flag.BoolVar(&conf.hab, "H", false, "set HAB fuses") flag.StringVar(&conf.otaELF, "o", "", "trusted applet payload") flag.StringVar(&conf.otaSig, "O", "", "trusted applet signature") @@ -171,6 +173,15 @@ func main() { } log.Printf("%s\n\n", s) } + case conf.crashLogs: + for _, d := range conf.devs { + log.Printf("👁️‍🗨️ @ %s", d.usb.Path) + s, err := d.crashLogs() + if err != nil { + log.Printf("Failed to get crash logs on %q: %c", d.usb.Path, err) + } + log.Printf("%s\n\n", s) + } case len(conf.otaELF) > 0 || len(conf.otaSig) > 0: if len(conf.devs) != 1 { log.Fatal("Please specify which device to OTA using -d") diff --git a/go.mod b/go.mod index e2b155c..e3b455a 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/coreos/go-semver v0.3.1 github.com/flynn/hid v0.0.0-20190502022136-f1b9b6cc019a github.com/flynn/u2f v0.0.0-20180613185708-15554eb68e5d - github.com/gsora/fidati v0.0.0-20220824075547-eeb0a5f7d6c3 + github.com/gsora/fidati v0.0.0-20230806170658-ab651720d7c3 github.com/smallnest/ringbuffer v0.0.0-20230728150354-35801fa39d0e github.com/transparency-dev/armored-witness-boot v0.0.0-20230904140406-e2e16c7665b7 github.com/transparency-dev/armored-witness-common v0.0.0-20240313170947-0b19d0fb8b95 diff --git a/go.sum b/go.sum index 99d03bd..7087809 100644 --- a/go.sum +++ b/go.sum @@ -21,8 +21,8 @@ github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/gsora/fidati v0.0.0-20220824075547-eeb0a5f7d6c3 h1:klG3scbSLaGIvJO1p9wdTaHonsCSAcvNrX8vfa8LRd4= -github.com/gsora/fidati v0.0.0-20220824075547-eeb0a5f7d6c3/go.mod h1:pqELFmXT+lU57T8pIGwPSOODIvRv/r/lwxlJX0UupvY= +github.com/gsora/fidati v0.0.0-20230806170658-ab651720d7c3 h1:zugXhdIprbuLMfR3ATkt5+YRx9VMBJgjPn1IDwluvJs= +github.com/gsora/fidati v0.0.0-20230806170658-ab651720d7c3/go.mod h1:pqELFmXT+lU57T8pIGwPSOODIvRv/r/lwxlJX0UupvY= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= diff --git a/trusted_os/ctl.go b/trusted_os/ctl.go index b0fdb3c..41e1489 100644 --- a/trusted_os/ctl.go +++ b/trusted_os/ctl.go @@ -15,6 +15,8 @@ package main import ( + "bytes" + "compress/gzip" "crypto/sha256" "encoding/hex" "errors" @@ -39,6 +41,9 @@ const ( MII_STATUS = 0x1 // Table 22–8, Status register bit definitions, 802.3-2008 STATUS_LINK = 2 + + // Max bytes to return via HID - we use 64 as a safe guess for protobuf wire overhead + maxChunkSize = api.MaxMessageSize - 64 ) // witnessStatus represents the latest view of the witness applet's status. @@ -54,6 +59,8 @@ type controlInterface struct { SRKHash string ota *otaBuffer + + logBuffer []byte } func getStatus() (s *api.Status) { @@ -154,9 +161,54 @@ func (ctl *controlInterface) HAB(_ []byte) []byte { return api.EmptyResponse() } -func (ctl *controlInterface) ConsoleLogs(_ []byte) (res []byte) { - logs := getConsoleLogs() - return []byte(logs) +func (ctl *controlInterface) handleLogsRequest(r []byte, l func() []byte) (res []byte) { + req := &api.LogMessagesRequest{} + if err := proto.Unmarshal(r, req); err != nil { + log.Printf("Failed to parse LogMessages request: %v", err) + return api.ErrorResponse(err) + } + if !req.Continue { + log.Printf("Grabbing log messages...") + logs := l() + ll := len(logs) + b := &bytes.Buffer{} + gz := gzip.NewWriter(b) + if _, err := gz.Write(logs); err != nil { + log.Printf("Failed to gzip logs: %v", err) + } + if err := gz.Close(); err != nil { + log.Printf("Failed to close gzip writer: %v", err) + + } + logs = nil + ctl.logBuffer = b.Bytes() + log.Printf("Compressed %d bytes of log messages to %d send", ll, len(ctl.logBuffer)) + } + ret := &api.LogMessagesResponse{} + if l := len(ctl.logBuffer); l > maxChunkSize { + ret.More = true + ret.Payload, ctl.logBuffer = ctl.logBuffer[:maxChunkSize], ctl.logBuffer[maxChunkSize:] + } else { + ret.More = false + ret.Payload = ctl.logBuffer + ctl.logBuffer = nil + } + b, _ := proto.Marshal(ret) + return b +} + +func (ctl *controlInterface) ConsoleLogs(r []byte) (res []byte) { + return ctl.handleLogsRequest(r, func() []byte { return getConsoleLogs() }) +} + +func (ctl *controlInterface) CrashLogs(r []byte) (res []byte) { + return ctl.handleLogsRequest(r, func() []byte { + l, err := retrieveLastCrashLog(ctl.RPC.Storage) + if err != nil { + return []byte(fmt.Sprintf("Failed to retrieve crash logs: %v", err)) + } + return l + }) } func (ctl *controlInterface) Start() { diff --git a/trusted_os/flash.go b/trusted_os/flash.go index 738a60d..8abebfd 100644 --- a/trusted_os/flash.go +++ b/trusted_os/flash.go @@ -15,6 +15,7 @@ package main import ( + "bytes" "errors" "fmt" "log" @@ -32,6 +33,7 @@ import ( "github.com/transparency-dev/armored-witness-os/api" ) +// imx6_usdhc: 15 GB/14 GiB card detected {MMC:true SD:false HC:true HS:true DDR:false Rate:150 BlockSize:512 Blocks:30576640 const ( expectedBlockSize = 512 // Expected size of MMC block in bytes otaLimit = 31457280 @@ -41,6 +43,8 @@ const ( osConfBlock = 0x5000 osBlockA = 0x5050 osBlockB = 0x102828 + crashLogBlock = 0x1D20000 // For storing contents of log ringbuffer on applet crash for later investigation. + crashLogNumBlocks = 0x400 // 1MB batchSize = 2048 ) @@ -342,6 +346,32 @@ func flashFirmware(storage Card, t FirmwareType, elf []byte, pb config.ProofBund return nil } +func storeAppletCrashLog(storage Card, l []byte) error { + log.Printf("SM storing applet exit log") + defer log.Printf("SM applet exit log stored") + + maxLogSize := crashLogNumBlocks * expectedBlockSize + if ll := len(l); ll > maxLogSize { + l = l[ll-maxLogSize:] + } else if ll < maxLogSize { + // Pad up so we overwrite all of any prior logging to avoid confusion. + l = append(l, make([]byte, maxLogSize-ll)...) + } + return storage.WriteBlocks(crashLogBlock, l) +} + +func retrieveLastCrashLog(storage Card) ([]byte, error) { + maxLogSize := crashLogNumBlocks * expectedBlockSize + r, err := storage.Read(crashLogBlock*expectedBlockSize, int64(maxLogSize)) + if err != nil { + return nil, err + } + if p := bytes.IndexByte(r, 0); p > 0 { + r = r[:p] + } + return r, nil +} + // Update is the handler for U2FHID_ARMORY_OTA requests, which consist of // applet updates. func (ctl *controlInterface) Update(req []byte) (res []byte) { diff --git a/trusted_os/load.go b/trusted_os/load.go index 82e508f..1a9d3a3 100644 --- a/trusted_os/load.go +++ b/trusted_os/load.go @@ -71,9 +71,7 @@ func loadApplet(elf []byte, ctl *controlInterface) (ta *monitor.ExecCtx, err err // enable FIQs bits.Clear(&ta.SPSR, CPSR_FIQ) - go run(ta) - - return + return ta, run(ta) } func run(ctx *monitor.ExecCtx) (err error) { diff --git a/trusted_os/main.go b/trusted_os/main.go index 2b70c28..0b1ec3f 100644 --- a/trusted_os/main.go +++ b/trusted_os/main.go @@ -228,12 +228,15 @@ func main() { usbarmory.LED("white", true) - ta, err := loadApplet(ta.Firmware, ctl) + appletCtx, err := loadApplet(ta.Firmware, ctl) if err != nil { log.Printf("SM applet execution error, %v", err) } - <-ta.Done() + <-appletCtx.Done() + if err := storeAppletCrashLog(Storage, getConsoleLogs()); err != nil { + log.Printf("Failed to store ringbuffer logs: %v", err) + } } }() } diff --git a/trusted_os/usb_hid.go b/trusted_os/usb_hid.go index 3293cec..46159ab 100644 --- a/trusted_os/usb_hid.go +++ b/trusted_os/usb_hid.go @@ -124,6 +124,9 @@ func configureHID(device *usb.Device, ctl *controlInterface) (err error) { if err = hid.AddMapping(api.U2FHID_ARMORY_CONSOLE_LOGS, ctl.ConsoleLogs); err != nil { return } + if err = hid.AddMapping(api.U2FHID_ARMORY_CRASH_LOGS, ctl.CrashLogs); err != nil { + return + } return }