llvm.org GIT mirror llvm / dc02554
Next step along the way to getting good error messages for bad archives. I consulted with Lang Hames on this work, and the goal was to add a bit of "where" in the archive the error occurred along with what the error was. So this step changes ArchiveMemberHeader into a class with a pointer to the archive header and the parent archive. Which allows the methods in the ArchiveMemberHeader to determine which member the header is for to include that information in the error message. For this first step the "where" is just the offset to the member in the archive. The next step will be a new method on ArchiveMemberHeader to get the full name, if possible, to be use in the error message. Which will now be possible as ArchiveMemberHeader contains a pointer to the Archive with its string table and its size, etc. so the full name can be determined from the header if it is valid. Also this change adds the missing checks the archive header is actually contained in the buffer and is not truncated, as well as if the terminating characters are correct in the header. And changes one error message in Archive::Child::getNext() where the name or offset to member is now added. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@276686 91177308-0d34-0410-b5e6-96231b3b80d8 Kevin Enderby 4 years ago
5 changed file(s) with 158 addition(s) and 46 deletion(s). Raw diff Collapse all Expand all
2424
2525 namespace llvm {
2626 namespace object {
27 struct ArchiveMemberHeader {
28 char Name[16];
29 char LastModified[12];
30 char UID[6];
31 char GID[6];
32 char AccessMode[8];
33 char Size[10]; ///< Size of data, not including header or padding.
34 char Terminator[2];
27
28 class Archive;
29
30 class ArchiveMemberHeader {
31 public:
32 friend class Archive;
33 ArchiveMemberHeader(Archive const *Parent, const char *RawHeaderPtr,
34 uint64_t Size, Error *Err);
35 // ArchiveMemberHeader() = default;
3536
3637 /// Get the name without looking up long names.
3738 llvm::StringRef getName() const;
4243 sys::fs::perms getAccessMode() const;
4344 sys::TimeValue getLastModified() const;
4445 llvm::StringRef getRawLastModified() const {
45 return StringRef(LastModified, sizeof(LastModified)).rtrim(' ');
46 return StringRef(ArMemHdr->LastModified,
47 sizeof(ArMemHdr->LastModified)).rtrim(' ');
4648 }
4749 unsigned getUID() const;
4850 unsigned getGID() const;
51
52 // This returns the size of the private struct ArMemHdrType
53 uint64_t getSizeOf() const {
54 return sizeof(ArMemHdrType);
55 }
56
57 private:
58 struct ArMemHdrType {
59 char Name[16];
60 char LastModified[12];
61 char UID[6];
62 char GID[6];
63 char AccessMode[8];
64 char Size[10]; ///< Size of data, not including header or padding.
65 char Terminator[2];
66 };
67 Archive const *Parent;
68 ArMemHdrType const *ArMemHdr;
4969 };
5070
5171 class Archive : public Binary {
5474 class Child {
5575 friend Archive;
5676 const Archive *Parent;
77 friend ArchiveMemberHeader;
78 ArchiveMemberHeader Header;
5779 /// \brief Includes header but not padding byte.
5880 StringRef Data;
5981 /// \brief Offset from Data to the start of the file.
6082 uint16_t StartOfFile;
6183
62 const ArchiveMemberHeader *getHeader() const {
63 return reinterpret_cast(Data.data());
64 }
65
6684 bool isThinMember() const;
6785
6886 public:
7997
8098 ErrorOr getName() const;
8199 ErrorOr getFullName() const;
82 StringRef getRawName() const { return getHeader()->getName(); }
100 StringRef getRawName() const { return Header.getName(); }
83101 sys::TimeValue getLastModified() const {
84 return getHeader()->getLastModified();
102 return Header.getLastModified();
85103 }
86104 StringRef getRawLastModified() const {
87 return getHeader()->getRawLastModified();
88 }
89 unsigned getUID() const { return getHeader()->getUID(); }
90 unsigned getGID() const { return getHeader()->getGID(); }
105 return Header.getRawLastModified();
106 }
107 unsigned getUID() const { return Header.getUID(); }
108 unsigned getGID() const { return Header.getGID(); }
91109 sys::fs::perms getAccessMode() const {
92 return getHeader()->getAccessMode();
110 return Header.getAccessMode();
93111 }
94112 /// \return the size of the archive member without the header or padding.
95113 Expected getSize() const;
3333 object_error::parse_failed);
3434 }
3535
36 ArchiveMemberHeader::ArchiveMemberHeader(const Archive *Parent,
37 const char *RawHeaderPtr,
38 uint64_t Size, Error *Err)
39 : Parent(Parent),
40 ArMemHdr(reinterpret_cast(RawHeaderPtr)) {
41 if (RawHeaderPtr == nullptr)
42 return;
43 ErrorAsOutParameter ErrAsOutParam(Err);
44
45 // TODO: For errors messages with the ArchiveMemberHeader class use the
46 // archive member name instead of the the offset to the archive member header.
47 // When there is also error getting the member name then use the offset to
48 // the member in the message.
49
50 if (Size < sizeof(ArMemHdrType)) {
51 if (Err) {
52 uint64_t Offset = RawHeaderPtr - Parent->getData().data();
53 *Err = malformedError("remaining size of archive too small for next "
54 "archive member header at offset " +
55 Twine(Offset));
56 }
57 return;
58 }
59 if (ArMemHdr->Terminator[0] != '`' || ArMemHdr->Terminator[1] != '\n') {
60 if (Err) {
61 std::string Buf;
62 raw_string_ostream OS(Buf);
63 OS.write_escaped(llvm::StringRef(ArMemHdr->Terminator,
64 sizeof(ArMemHdr->Terminator)));
65 OS.flush();
66 uint64_t Offset = RawHeaderPtr - Parent->getData().data();
67 *Err = malformedError("terminator characters in archive member \"" + Buf +
68 "\" not the correct \"`\\n\" values for the "
69 "archive member header at offset " + Twine(Offset));
70 }
71 return;
72 }
73 }
74
3675 StringRef ArchiveMemberHeader::getName() const {
3776 char EndCond;
38 if (Name[0] == '/' || Name[0] == '#')
77 if (ArMemHdr->Name[0] == '/' || ArMemHdr->Name[0] == '#')
3978 EndCond = ' ';
4079 else
4180 EndCond = '/';
4281 llvm::StringRef::size_type end =
43 llvm::StringRef(Name, sizeof(Name)).find(EndCond);
82 llvm::StringRef(ArMemHdr->Name, sizeof(ArMemHdr->Name)).find(EndCond);
4483 if (end == llvm::StringRef::npos)
45 end = sizeof(Name);
46 assert(end <= sizeof(Name) && end > 0);
84 end = sizeof(ArMemHdr->Name);
85 assert(end <= sizeof(ArMemHdr->Name) && end > 0);
4786 // Don't include the EndCond if there is one.
48 return llvm::StringRef(Name, end);
87 return llvm::StringRef(ArMemHdr->Name, end);
4988 }
5089
5190 Expected ArchiveMemberHeader::getSize() const {
5291 uint32_t Ret;
53 if (llvm::StringRef(Size, sizeof(Size)).rtrim(" ").getAsInteger(10, Ret)) {
92 if (llvm::StringRef(ArMemHdr->Size,
93 sizeof(ArMemHdr->Size)).rtrim(" ").getAsInteger(10, Ret)) {
5494 std::string Buf;
5595 raw_string_ostream OS(Buf);
56 OS.write_escaped(llvm::StringRef(Size, sizeof(Size)).rtrim(" "));
96 OS.write_escaped(llvm::StringRef(ArMemHdr->Size,
97 sizeof(ArMemHdr->Size)).rtrim(" "));
5798 OS.flush();
99 uint64_t Offset = reinterpret_cast(ArMemHdr) -
100 Parent->getData().data();
58101 return malformedError("characters in size field in archive header are not "
59 "all decimal numbers: '" + Buf + "'");
102 "all decimal numbers: '" + Buf + "' for archive "
103 "member header at offset " + Twine(Offset));
60104 }
61105 return Ret;
62106 }
63107
64108 sys::fs::perms ArchiveMemberHeader::getAccessMode() const {
65109 unsigned Ret;
66 if (StringRef(AccessMode, sizeof(AccessMode)).rtrim(' ').getAsInteger(8, Ret))
110 if (StringRef(ArMemHdr->AccessMode,
111 sizeof(ArMemHdr->AccessMode)).rtrim(' ').getAsInteger(8, Ret))
67112 llvm_unreachable("Access mode is not an octal number.");
68113 return static_cast(Ret);
69114 }
70115
71116 sys::TimeValue ArchiveMemberHeader::getLastModified() const {
72117 unsigned Seconds;
73 if (StringRef(LastModified, sizeof(LastModified)).rtrim(' ')
118 if (StringRef(ArMemHdr->LastModified,
119 sizeof(ArMemHdr->LastModified)).rtrim(' ')
74120 .getAsInteger(10, Seconds))
75121 llvm_unreachable("Last modified time not a decimal number.");
76122
81127
82128 unsigned ArchiveMemberHeader::getUID() const {
83129 unsigned Ret;
84 StringRef User = StringRef(UID, sizeof(UID)).rtrim(' ');
130 StringRef User = StringRef(ArMemHdr->UID, sizeof(ArMemHdr->UID)).rtrim(' ');
85131 if (User.empty())
86132 return 0;
87133 if (User.getAsInteger(10, Ret))
91137
92138 unsigned ArchiveMemberHeader::getGID() const {
93139 unsigned Ret;
94 StringRef Group = StringRef(GID, sizeof(GID)).rtrim(' ');
140 StringRef Group = StringRef(ArMemHdr->GID, sizeof(ArMemHdr->GID)).rtrim(' ');
95141 if (Group.empty())
96142 return 0;
97143 if (Group.getAsInteger(10, Ret))
101147
102148 Archive::Child::Child(const Archive *Parent, StringRef Data,
103149 uint16_t StartOfFile)
104 : Parent(Parent), Data(Data), StartOfFile(StartOfFile) {}
150 : Parent(Parent), Header(Parent, Data.data(), Data.size(), nullptr),
151 Data(Data), StartOfFile(StartOfFile) {
152 }
105153
106154 Archive::Child::Child(const Archive *Parent, const char *Start, Error *Err)
107 : Parent(Parent) {
155 : Parent(Parent), Header(Parent, Start, Parent->getData().size() -
156 (Start - Parent->getData().data()), Err) {
108157 if (!Start)
109158 return;
110159 ErrorAsOutParameter ErrAsOutParam(Err);
111160
112 uint64_t Size = sizeof(ArchiveMemberHeader);
161 // If there was an error in the construction of the Header and we were passed
162 // Err that is not nullptr then just return with the error now set.
163 if (Err && *Err)
164 return;
165
166 uint64_t Size = Header.getSizeOf();
113167 Data = StringRef(Start, Size);
114168 if (!isThinMember()) {
115169 Expected MemberSize = getRawSize();
123177 }
124178
125179 // Setup StartOfFile and PaddingBytes.
126 StartOfFile = sizeof(ArchiveMemberHeader);
180 StartOfFile = Header.getSizeOf();
127181 // Don't include attached name.
128182 StringRef Name = getRawName();
129183 if (Name.startswith("#1/")) {
136190
137191 Expected Archive::Child::getSize() const {
138192 if (Parent->IsThin) {
139 Expected Size = getHeader()->getSize();
193 Expected Size = Header.getSize();
140194 if (!Size)
141195 return Size.takeError();
142196 return Size.get();
145199 }
146200
147201 Expected Archive::Child::getRawSize() const {
148 return getHeader()->getSize();
202 return Header.getSize();
149203 }
150204
151205 bool Archive::Child::isThinMember() const {
152 StringRef Name = getHeader()->getName();
206 StringRef Name = Header.getName();
153207 return Parent->IsThin && Name != "/" && Name != "//";
154208 }
155209
199253 return Child(Parent, nullptr, nullptr);
200254
201255 // Check to see if this is past the end of the archive.
202 if (NextLoc > Parent->Data.getBufferEnd())
203 return malformedError("offset to next archive member past the end of the "
204 "archive");
256 if (NextLoc > Parent->Data.getBufferEnd()) {
257 Twine Msg("offset to next archive member past the end of the archive after "
258 "member ");
259 ErrorOr NameOrErr = getName();
260 if (NameOrErr.getError()) {
261 uint64_t Offset = Data.data() - Parent->getData().data();
262 return malformedError(Msg + "at offset " + Twine(Offset));
263 } else
264 return malformedError(Msg + Twine(NameOrErr.get()));
265 }
205266
206267 Error Err;
207268 Child Ret(Parent, NextLoc, &Err);
246307 uint64_t name_size;
247308 if (name.substr(3).rtrim(' ').getAsInteger(10, name_size))
248309 llvm_unreachable("Long name length is not an ingeter");
249 return Data.substr(sizeof(ArchiveMemberHeader), name_size).rtrim('\0');
310 return Data.substr(Header.getSizeOf(), name_size).rtrim('\0');
250311 } else {
251312 // It is not a long name so trim the blanks at the end of the name.
252313 if (name[name.size() - 1] != '/') {
0 !
1 hello.c 1444941273 124 0 100644 102 `
2 #include
3 #include
4 int
5 main()
6 {
7 printf("Hello World\n");
8 return EXIT_SUCCESS;
9 }
10 foo.c 1444941645 124 0 100644
0 !
1 hello.c 1444941273 124 0 100644 102 @
2 #include
3 #include
4 int
5 main()
6 {
7 printf("Hello World\n");
8 return EXIT_SUCCESS;
9 }
44 # RUN: %p/Inputs/libbogus1.a \
55 # RUN: 2>&1 | FileCheck -check-prefix=bogus1 %s
66
7 # bogus1: libbogus1.a': truncated or malformed archive (characters in size field in archive header are not all decimal numbers: '10%')
7 # bogus1: libbogus1.a': truncated or malformed archive (characters in size field in archive header are not all decimal numbers: '10%' for archive member header at offset 8)
88
99 # RUN: not llvm-objdump -macho -archive-headers \
1010 # RUN: %p/Inputs/libbogus2.a \
1111 # RUN: 2>&1 | FileCheck -check-prefix=bogus2 %s
1212
13 # bogus2: libbogus2.a': truncated or malformed archive (characters in size field in archive header are not all decimal numbers: '1%')
13 # bogus2: libbogus2.a': truncated or malformed archive (characters in size field in archive header are not all decimal numbers: '1%' for archive member header at offset 170)
1414
1515 # RUN: not llvm-objdump -macho -archive-headers \
1616 # RUN: %p/Inputs/libbogus3.a \
1717 # RUN: 2>&1 | FileCheck -check-prefix=bogus3 %s
1818
19 # bogus3: libbogus3.a': truncated or malformed archive (offset to next archive member past the end of the archive)
19 # bogus3: libbogus3.a': truncated or malformed archive (offset to next archive member past the end of the archive after member foo.c)
20
21 # RUN: not llvm-objdump -macho -archive-headers \
22 # RUN: %p/Inputs/libbogus4.a \
23 # RUN: 2>&1 | FileCheck -check-prefix=bogus4 %s
24
25 # bogus4: libbogus4.a': truncated or malformed archive (remaining size of archive too small for next archive member header at offset 170)
26
27 # RUN: not llvm-objdump -macho -archive-headers \
28 # RUN: %p/Inputs/libbogus5.a \
29 # RUN: 2>&1 | FileCheck -check-prefix=bogus5 %s
30
31 # bogus5: libbogus5.a': truncated or malformed archive (terminator characters in archive member "@\n" not the correct "`\n" values for the archive member header at offset 8)