1
2
3
4
5 package buildid
6
7 import (
8 "bytes"
9 "crypto/sha256"
10 "debug/elf"
11 "encoding/binary"
12 "internal/obscuretestdata"
13 "os"
14 "reflect"
15 "strings"
16 "testing"
17 )
18
19 const (
20 expectedID = "abcdefghijklmnopqrstuvwxyz.1234567890123456789012345678901234567890123456789012345678901234"
21 newID = "bcdefghijklmnopqrstuvwxyza.2345678901234567890123456789012345678901234567890123456789012341"
22 )
23
24 func TestReadFile(t *testing.T) {
25 f, err := os.CreateTemp("", "buildid-test-")
26 if err != nil {
27 t.Fatal(err)
28 }
29 tmp := f.Name()
30 defer os.Remove(tmp)
31 f.Close()
32
33
34
35
36
37 var files = []string{
38 "p.a.base64",
39 "a.elf.base64",
40 "a.macho.base64",
41 "a.pe.base64",
42 }
43
44 for _, name := range files {
45 f, err := obscuretestdata.DecodeToTempFile("testdata/" + name)
46 if err != nil {
47 t.Errorf("obscuretestdata.DecodeToTempFile(testdata/%s): %v", name, err)
48 continue
49 }
50 defer os.Remove(f)
51 id, err := ReadFile(f)
52 if id != expectedID || err != nil {
53 t.Errorf("ReadFile(testdata/%s) = %q, %v, want %q, nil", f, id, err, expectedID)
54 }
55 old := readSize
56 readSize = 2048
57 id, err = ReadFile(f)
58 readSize = old
59 if id != expectedID || err != nil {
60 t.Errorf("ReadFile(%s) [readSize=2k] = %q, %v, want %q, nil", f, id, err, expectedID)
61 }
62
63 data, err := os.ReadFile(f)
64 if err != nil {
65 t.Fatal(err)
66 }
67 m, _, err := FindAndHash(bytes.NewReader(data), expectedID, 1024)
68 if err != nil {
69 t.Errorf("FindAndHash(%s): %v", f, err)
70 continue
71 }
72 if err := os.WriteFile(tmp, data, 0666); err != nil {
73 t.Error(err)
74 continue
75 }
76 tf, err := os.OpenFile(tmp, os.O_WRONLY, 0)
77 if err != nil {
78 t.Error(err)
79 continue
80 }
81 err = Rewrite(tf, m, newID)
82 err2 := tf.Close()
83 if err != nil {
84 t.Errorf("Rewrite(%s): %v", f, err)
85 continue
86 }
87 if err2 != nil {
88 t.Fatal(err2)
89 }
90
91 id, err = ReadFile(tmp)
92 if id != newID || err != nil {
93 t.Errorf("ReadFile(%s after Rewrite) = %q, %v, want %q, nil", f, id, err, newID)
94 }
95
96
97
98 if strings.Contains(name, "elf") {
99
100 if elf.Class(data[elf.EI_CLASS]) != elf.ELFCLASS64 {
101 continue
102 }
103
104
105 if elf.Data(data[elf.EI_DATA]) != elf.ELFDATA2LSB {
106 continue
107 }
108 order := binary.LittleEndian
109
110 var hdr elf.Header64
111 if err := binary.Read(bytes.NewReader(data), order, &hdr); err != nil {
112 t.Error(err)
113 continue
114 }
115
116 phoff := hdr.Phoff
117 phnum := int(hdr.Phnum)
118 phsize := uint64(hdr.Phentsize)
119
120 for i := 0; i < phnum; i++ {
121 var phdr elf.Prog64
122 if err := binary.Read(bytes.NewReader(data[phoff:]), order, &phdr); err != nil {
123 t.Error(err)
124 continue
125 }
126
127 if elf.ProgType(phdr.Type) == elf.PT_NOTE {
128
129
130 order.PutUint64(data[phoff+4*8:], phdr.Filesz+1)
131
132
133 order.PutUint64(data[phoff+6*8:], 0)
134
135
136
137 order.PutUint32(data[phdr.Off+12:], 0)
138 }
139
140 phoff += phsize
141 }
142
143 if err := os.WriteFile(tmp, data, 0666); err != nil {
144 t.Error(err)
145 continue
146 }
147
148 id, err := ReadFile(tmp)
149
150
151
152
153 if id != "" || err != nil {
154 t.Errorf("ReadFile with zero ELF Align = %q, %v, want %q, nil", id, err, "")
155 continue
156 }
157 }
158 }
159 }
160
161 func TestFindAndHash(t *testing.T) {
162 buf := make([]byte, 64)
163 buf2 := make([]byte, 64)
164 id := make([]byte, 8)
165 zero := make([]byte, 8)
166 for i := range id {
167 id[i] = byte(i)
168 }
169 numError := 0
170 errorf := func(msg string, args ...any) {
171 t.Errorf(msg, args...)
172 if numError++; numError > 20 {
173 t.Logf("stopping after too many errors")
174 t.FailNow()
175 }
176 }
177 for bufSize := len(id); bufSize <= len(buf); bufSize++ {
178 for j := range buf {
179 for k := 0; k < 2*len(id) && j+k < len(buf); k++ {
180 for i := range buf {
181 buf[i] = 1
182 }
183 copy(buf[j:], id)
184 copy(buf[j+k:], id)
185 var m []int64
186 if j+len(id) <= j+k {
187 m = append(m, int64(j))
188 }
189 if j+k+len(id) <= len(buf) {
190 m = append(m, int64(j+k))
191 }
192 copy(buf2, buf)
193 for _, p := range m {
194 copy(buf2[p:], zero)
195 }
196 h := sha256.Sum256(buf2)
197
198 matches, hash, err := FindAndHash(bytes.NewReader(buf), string(id), bufSize)
199 if err != nil {
200 errorf("bufSize=%d j=%d k=%d: findAndHash: %v", bufSize, j, k, err)
201 continue
202 }
203 if !reflect.DeepEqual(matches, m) {
204 errorf("bufSize=%d j=%d k=%d: findAndHash: matches=%v, want %v", bufSize, j, k, matches, m)
205 continue
206 }
207 if hash != h {
208 errorf("bufSize=%d j=%d k=%d: findAndHash: matches correct, but hash=%x, want %x", bufSize, j, k, hash, h)
209 }
210 }
211 }
212 }
213 }
214
215 func TestExcludedReader(t *testing.T) {
216 const s = "0123456789abcdefghijklmn"
217 tests := []struct {
218 start, end int64
219 results []string
220 }{
221 {12, 15, []string{"0123456789", "ab\x00\x00\x00fghij", "klmn"}},
222 {8, 21, []string{"01234567\x00\x00", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "\x00lmn"}},
223 {10, 20, []string{"0123456789", "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", "klmn"}},
224 {0, 5, []string{"\x00\x00\x00\x00\x0056789", "abcdefghij", "klmn"}},
225 {12, 24, []string{"0123456789", "ab\x00\x00\x00\x00\x00\x00\x00\x00", "\x00\x00\x00\x00"}},
226 }
227 p := make([]byte, 10)
228 for _, test := range tests {
229 r := &excludedReader{strings.NewReader(s), 0, test.start, test.end}
230 for _, res := range test.results {
231 n, err := r.Read(p)
232 if err != nil {
233 t.Errorf("read failed: %v", err)
234 }
235 if n != len(res) {
236 t.Errorf("unexpected number of bytes read: want %d, got %d", len(res), n)
237 }
238 if string(p[:n]) != res {
239 t.Errorf("unexpected bytes: want %q, got %q", res, p[:n])
240 }
241 }
242 }
243 }
244
245 func TestEmptyID(t *testing.T) {
246 r := strings.NewReader("aha!")
247 matches, hash, err := FindAndHash(r, "", 1000)
248 if matches != nil || hash != ([32]byte{}) || err == nil || !strings.Contains(err.Error(), "no id") {
249 t.Errorf("FindAndHash: want nil, [32]byte{}, no id specified, got %v, %v, %v", matches, hash, err)
250 }
251 }
252
View as plain text