Skip to content

Commit acc2cfd

Browse files
committed
Improve libformat_parser FFI
This should remove a use-after-free as well as simplify the FFI interface. gcc/rust/ChangeLog: * ast/rust-fmt.cc (Pieces::collect): Handle changes to ffi interface. (Pieces::~Pieces): Remove function definition. (Pieces::Pieces): Likewise. (Pieces::operator=): Likewise. * ast/rust-fmt.h: Include "optional.h". (rust_ffi_alloc): New extern "C" function declaration. (rust_ffi_dealloc): Likewise. (class FFIVec): New class. (class FFIOpt): Likewise. (RustHamster::RustHamster): New constructor accepting const std::string reference. (struct FormatSpec): Use FFIOpt. (struct PieceSlice): Remove struct. (struct RustString): Likewise. (struct FormatArgsHandle): Likewise. (collect_pieces): Change function signature. (clone_pieces): Likewise. (destroy_pieces): Remove extern "C" function declaration. (Pieces::~Pieces): Remove function declaration. (Pieces::operator=): Likewise. (Pieces::get_pieces): Handle changes to class fields. (Pieces::Pieces): Remove copy and move constructor declarations, adjust signature of remaining constructor declaration. (Pieces::pieces_vector): Remove member variable. (Pieces::handle): Likewise. (Pieces::data): Add member variable. * expand/rust-macro-builtins-asm.cc (expand_inline_asm_strings): Use references to avoid copying. libgrust/ChangeLog: * libformat_parser/src/lib.rs (struct FFIVec): New. (trait StringLeakExt): Remove. (struct FFIOpt): New. (trait IntoFFI): Adjust implementation for Option. (struct RustHamster): Add lifetime and adjust conversion to and from &str. (enum Piece): Adjust definition to handle changes to RustHamster. (struct Argument): Likewise. (struct FormatSpec): Use FFIOpt and RustHamster. (enum Position): Use RustHamster. (enum Count): Likewise. (struct PieceSlice): Replace with... (typedef PieceVec): ...this. (struct RustString): Remove. (struct FormatArgsHandle): Likewise. (fn collect_pieces): Adjust signature, greatly simplifying implementation. (fn clone_pieces): Likewise. (fn destroy_pieces): Remove. (trait LayoutExt): New. (fn rust_ffi_alloc): New. (fn rust_ffi_dealloc): New. Signed-off-by: Owen Avery <[email protected]>
1 parent 5b7a0d7 commit acc2cfd

File tree

4 files changed

+378
-217
lines changed

4 files changed

+378
-217
lines changed

gcc/rust/ast/rust-fmt.cc

Lines changed: 4 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -32,41 +32,12 @@ Pieces
3232
Pieces::collect (const std::string &to_parse, bool append_newline,
3333
ffi::ParseMode parse_mode)
3434
{
35-
auto handle
36-
= ffi::collect_pieces (to_parse.c_str (), append_newline, parse_mode);
35+
auto to_parse_dupe = to_parse;
36+
auto pieces = ffi::collect_pieces (ffi::RustHamster (to_parse_dupe),
37+
append_newline, parse_mode);
3738

38-
// this performs multiple copies, can we avoid them maybe?
39-
// TODO: Instead of just creating a vec of, basically, `ffi::Piece`s, we
40-
// should transform them into the proper C++ type which we can work with. so
41-
// transform all the strings into C++ strings? all the Option<T> into
42-
// tl::optional<T>?
43-
auto pieces_vector = std::vector<ffi::Piece> (handle.piece_slice.base_ptr,
44-
handle.piece_slice.base_ptr
45-
+ handle.piece_slice.len);
46-
47-
return Pieces (handle, std::move (pieces_vector));
48-
}
49-
50-
Pieces::~Pieces () { ffi::destroy_pieces (handle); }
51-
52-
Pieces::Pieces (const Pieces &other) : pieces_vector (other.pieces_vector)
53-
{
54-
handle = ffi::clone_pieces (other.handle);
39+
return Pieces (std::move (to_parse_dupe), std::move (pieces));
5540
}
5641

57-
Pieces &
58-
Pieces::operator= (const Pieces &other)
59-
{
60-
handle = ffi::clone_pieces (other.handle);
61-
pieces_vector = other.pieces_vector;
62-
63-
return *this;
64-
}
65-
66-
Pieces::Pieces (Pieces &&other)
67-
: pieces_vector (std::move (other.pieces_vector)),
68-
handle (clone_pieces (other.handle))
69-
{}
70-
7142
} // namespace Fmt
7243
} // namespace Rust

gcc/rust/ast/rust-fmt.h

Lines changed: 139 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,138 @@
2020
#define RUST_FMT_H
2121

2222
#include "rust-system.h"
23-
24-
// FIXME: How to encode Option?
23+
#include "optional.h"
2524

2625
namespace Rust {
2726
namespace Fmt {
2827

2928
namespace ffi {
3029

30+
extern "C" {
31+
32+
unsigned char *rust_ffi_alloc (size_t count, size_t elem_size, size_t align);
33+
34+
void rust_ffi_dealloc (unsigned char *data, size_t count, size_t elem_size,
35+
size_t align);
36+
37+
} // extern "C"
38+
39+
template <typename T> class FFIVec
40+
{
41+
T *data;
42+
size_t len;
43+
size_t cap;
44+
45+
public:
46+
FFIVec () : data (alignof (T)), len (0), cap (0) {}
47+
48+
FFIVec (const FFIVec &) = delete;
49+
FFIVec &operator= (const FFIVec &) = delete;
50+
51+
FFIVec (FFIVec &&other) : data (other.data), len (other.len), cap (other.cap)
52+
{
53+
other.data = (T *) alignof (T);
54+
other.len = 0;
55+
other.cap = 0;
56+
}
57+
58+
FFIVec &operator= (FFIVec &&other)
59+
{
60+
this->~FFIVec ();
61+
new (this) FFIVec (std::move (other));
62+
return *this;
63+
}
64+
65+
~FFIVec ()
66+
{
67+
// T can't be zero-sized
68+
if (cap)
69+
rust_ffi_dealloc ((unsigned char *) data, cap, sizeof (T), alignof (T));
70+
}
71+
72+
size_t size () const { return len; }
73+
74+
const T &operator[] (size_t idx) const
75+
{
76+
rust_assert (idx <= len);
77+
return data[idx];
78+
}
79+
80+
T *begin () { return data; }
81+
const T *begin () const { return data; }
82+
T *end () { return data + len; }
83+
const T *end () const { return data + len; }
84+
};
85+
86+
template <typename T> class FFIOpt
87+
{
88+
struct alignas (T) Inner
89+
{
90+
char data[sizeof (T)];
91+
} inner;
92+
bool is_some;
93+
94+
public:
95+
template <typename U> FFIOpt (U &&val) : is_some (true)
96+
{
97+
new (inner.data) T (std::forward<U> (val));
98+
}
99+
100+
FFIOpt () : is_some (false) {}
101+
102+
FFIOpt (const FFIOpt &other) : is_some (other.is_some)
103+
{
104+
if (is_some)
105+
new (inner.data) T (*(const T *) other.inner.data);
106+
}
107+
108+
FFIOpt (FFIOpt &&other) : is_some (other.is_some)
109+
{
110+
if (is_some)
111+
new (inner.data) T (std::move (*(const T *) other.inner.data));
112+
}
113+
114+
~FFIOpt ()
115+
{
116+
if (is_some)
117+
((T *) inner.data)->~T ();
118+
}
119+
120+
FFIOpt &operator= (const FFIOpt &other)
121+
{
122+
this->~FFIOpt ();
123+
new (this) FFIOpt (other);
124+
return *this;
125+
}
126+
127+
FFIOpt &operator= (FFIOpt &&other)
128+
{
129+
this->~FFIOpt ();
130+
new (this) FFIOpt (std::move (other));
131+
return *this;
132+
}
133+
134+
tl::optional<std::reference_wrapper<T>> get_opt ()
135+
{
136+
return (T *) inner.data;
137+
}
138+
139+
tl::optional<std::reference_wrapper<const T>> get_opt () const
140+
{
141+
return (const T *) inner.data;
142+
}
143+
};
144+
31145
struct RustHamster
32146
{
33147
const char *ptr;
34148
size_t len;
35149

36150
std::string to_string () const;
151+
152+
explicit RustHamster (const std::string &str)
153+
: ptr (str.data ()), len (str.size ())
154+
{}
37155
};
38156

39157
/// Enum of alignments which are supported.
@@ -166,33 +284,33 @@ struct Count
166284
struct FormatSpec
167285
{
168286
/// Optionally specified character to fill alignment with.
169-
const uint32_t *fill;
287+
FFIOpt<uint32_t> fill;
170288
/// Span of the optionally specified fill character.
171-
const InnerSpan *fill_span;
289+
FFIOpt<InnerSpan> fill_span;
172290
/// Optionally specified alignment.
173291
Alignment align;
174292
/// The `+` or `-` flag.
175-
const Sign *sign;
293+
FFIOpt<Sign> sign;
176294
/// The `#` flag.
177295
bool alternate;
178296
/// The `0` flag.
179297
bool zero_pad;
180298
/// The `x` or `X` flag. (Only for `Debug`.)
181-
const DebugHex *debug_hex;
299+
FFIOpt<DebugHex> debug_hex;
182300
/// The integer precision to use.
183301
Count precision;
184302
/// The span of the precision formatting flag (for diagnostics).
185-
const InnerSpan *precision_span;
303+
FFIOpt<InnerSpan> precision_span;
186304
/// The string width requested for the resulting format.
187305
Count width;
188306
/// The span of the width formatting flag (for diagnostics).
189-
const InnerSpan *width_span;
307+
FFIOpt<InnerSpan> width_span;
190308
/// The descriptor string representing the name of the format desired for
191309
/// this argument, this can be empty or any number of characters, although
192310
/// it is required to be one word.
193311
RustHamster ty;
194312
/// The span of the descriptor string (for diagnostics).
195-
const InnerSpan *ty_span;
313+
FFIOpt<InnerSpan> ty_span;
196314
};
197315

198316
/// Representation of an argument specification.
@@ -238,26 +356,6 @@ struct Piece
238356
};
239357
};
240358

241-
struct PieceSlice
242-
{
243-
const Piece *base_ptr;
244-
size_t len;
245-
size_t cap;
246-
};
247-
248-
struct RustString
249-
{
250-
const unsigned char *ptr;
251-
size_t len;
252-
size_t cap;
253-
};
254-
255-
struct FormatArgsHandle
256-
{
257-
PieceSlice piece_slice;
258-
RustString rust_string;
259-
};
260-
261359
enum ParseMode
262360
{
263361
Format = 0,
@@ -266,12 +364,10 @@ enum ParseMode
266364

267365
extern "C" {
268366

269-
FormatArgsHandle collect_pieces (const char *input, bool append_newline,
270-
ParseMode parse_mode);
367+
FFIVec<Piece> collect_pieces (RustHamster input, bool append_newline,
368+
ParseMode parse_mode);
271369

272-
FormatArgsHandle clone_pieces (const FormatArgsHandle &);
273-
274-
void destroy_pieces (FormatArgsHandle);
370+
FFIVec<Piece> clone_pieces (const FFIVec<Piece> &);
275371

276372
} // extern "C"
277373

@@ -281,33 +377,20 @@ struct Pieces
281377
{
282378
static Pieces collect (const std::string &to_parse, bool append_newline,
283379
ffi::ParseMode parse_mode);
284-
~Pieces ();
285-
286-
Pieces (const Pieces &other);
287-
Pieces &operator= (const Pieces &other);
288380

289-
Pieces (Pieces &&other);
290-
291-
const std::vector<ffi::Piece> &get_pieces () const { return pieces_vector; }
292-
293-
// {
294-
// slice = clone_pieces (&other.slice);
295-
// to_parse = other.to_parse;
296-
297-
// return *this;
298-
// }
381+
const ffi::FFIVec<ffi::Piece> &get_pieces () const { return data->second; }
299382

300383
private:
301-
Pieces (ffi::FormatArgsHandle handle, std::vector<ffi::Piece> &&pieces_vector)
302-
: pieces_vector (std::move (pieces_vector)), handle (handle)
384+
Pieces (std::string str, ffi::FFIVec<ffi::Piece> pieces)
385+
: data (
386+
std::make_shared<decltype (data)::element_type> (std::move (str),
387+
std::move (pieces)))
303388
{}
304389

305-
std::vector<ffi::Piece> pieces_vector;
306-
307-
// this memory is held for FFI reasons - it needs to be released and cloned
308-
// precisely, so try to not access it/modify it if possible. you should
309-
// instead work with `pieces_vector`
310-
ffi::FormatArgsHandle handle;
390+
// makes copying simpler
391+
// also, we'd need to keep the parsed string in a shared_ptr anyways
392+
// since we store pointers into the parsed string
393+
std::shared_ptr<std::pair<std::string, ffi::FFIVec<ffi::Piece>>> data;
311394
};
312395

313396
} // namespace Fmt

gcc/rust/expand/rust-macro-builtins-asm.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -787,12 +787,12 @@ expand_inline_asm_strings (InlineAsmContext inline_asm_ctx)
787787

788788
auto pieces = Fmt::Pieces::collect (template_str.symbol, false,
789789
Fmt::ffi::ParseMode::InlineAsm);
790-
auto pieces_vec = pieces.get_pieces ();
790+
auto &pieces_vec = pieces.get_pieces ();
791791

792792
std::string transformed_template_str = "";
793793
for (size_t i = 0; i < pieces_vec.size (); i++)
794794
{
795-
auto piece = pieces_vec[i];
795+
auto &piece = pieces_vec[i];
796796
if (piece.tag == Fmt::ffi::Piece::Tag::String)
797797
{
798798
transformed_template_str += piece.string._0.to_string ();

0 commit comments

Comments
 (0)