{"record":{"id":"7e2bd9c23925ac99","repo":"d2lang/d2","slug":"invalid-s-table-bounds","errorCode":null,"errorMessage":"invalid %s table bounds","messagePattern":"invalid (.+?) table bounds","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/textmeasure/fontface.go","lineNumber":218,"sourceCode":"\t\tif base < 0 || base+12 > len(src) {\n\t\t\treturn nil, fmt.Errorf(\"invalid font collection offset\")\n\t\t}\n\t}\n\n\tn := int(binary.BigEndian.Uint16(src[base+4 : base+6]))\n\trecords := base + 12\n\tif n > (len(src)-records)/16 {\n\t\treturn nil, fmt.Errorf(\"invalid sfnt table directory\")\n\t}\n\tfor i := 0; i < n; i++ {\n\t\trecord := src[records+16*i : records+16*(i+1)]\n\t\tif string(record[:4]) != tag {\n\t\t\tcontinue\n\t\t}\n\t\toffset := int(binary.BigEndian.Uint32(record[8:12]))\n\t\tlength := int(binary.BigEndian.Uint32(record[12:16]))\n\t\tif offset < 0 || length < 0 || offset > len(src)-length {\n\t\t\treturn nil, fmt.Errorf(\"invalid %s table bounds\", tag)\n\t\t}\n\t\treturn src[offset : offset+length], nil\n\t}\n\treturn nil, nil\n}\n","sourceCodeStart":200,"sourceCodeEnd":224,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/lib/textmeasure/fontface.go#L200-L224","documentation":"sfntTable parses the sfnt table directory of a TrueType/OpenType font to extract a named table (e.g. 'kern'). It validates that the table's offset and length from the directory record fall within the raw font bytes before slicing. If offset/length would go outside src (including overflow making offset negative), it returns this error instead of panicking on a bad slice.","triggerScenarios":"Calling d2fonts/fontface loading with a corrupt or truncated .ttf/.otf file, a hand-crafted/malicious font whose table directory declares an offset+length beyond the file, or a font whose Uint32 offset overflows int on 32-bit platforms. Reached via parseLegacyKern while initializing a FontFace for text measurement.","commonSituations":"Embedding a partially-downloaded or git-lfs-pointer font file; bundling a corrupted font asset; fuzzed/hostile font input; wasm builds where int is 32-bit and large offsets overflow negative.","solutions":["Verify the font file is a complete, valid TTF/OTF (e.g. open with fonttools or check magic bytes 0x00010000/Otto/true/ttcf)","Re-download or re-copy the font asset; check size against upstream","If fonts ship via git-lfs, ensure files are actually materialized, not pointer stubs","If processing untrusted fonts, treat this error as a rejection of the input rather than a bug"],"exampleFix":"// before\nraw, _ := os.ReadFile(\"font.ttf\") // truncated file\nface, err := NewFontFace(raw)\n// after\ninfo, _ := os.Stat(\"font.ttf\")\nif info.Size() < 12 { return fmt.Errorf(\"font file truncated\") }\nface, err := NewFontFace(raw)","handlingStrategy":"validation","validationCode":"func validateTTF(src []byte) error {\n    if len(src) < 12 { return fmt.Errorf(\"font too short\") }\n    return nil // plus check non-truncated file size before use\n}","typeGuard":"func looksLikeSFNT(src []byte) bool {\n    if len(src) < 12 { return false }\n    switch string(src[:4]) {\n    case \"\\x00\\x01\\x00\\x00\", \"Otto\", \"true\", \"ttcf\":\n        return true\n    }\n    return false\n}","tryCatchPattern":"table, err := sfntTable(src, \"kern\")\nif err != nil {\n    log.Printf(\"rejecting font: %v\", err)\n    return ErrBadFontAsset\n}","preventionTips":["Verify font file sizes/checksums at build or download time","Avoid git-lfs pointer stubs reaching production embeds","Never feed untrusted font bytes without expecting rejection errors","Test font assets load at app startup, not first render"],"tags":["fonts","parsing","bounds-check","corrupt-input"],"backgroundTag":"invalid-font-table-bounds","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}