Skip to content

Commit 47e4d7c

Browse files
authored
Change syntax of *_bounds_cast operators and add examples. (checkedc#256)
The existing syntax of the *_bound_cast operators is confusing. The kind of bounds being specified is not explicit. It depends on the number of arguments to the operator. It is clearer to just use a bounds expression to describe the bounds. That involves a little more typing, but it is easier to understand and allows programmers to use all the different variants of bounds expressions. Add examples of using dynamic_bounds_cast and assume_bounds_cast for clarity.
1 parent 689bc4e commit 47e4d7c

File tree

1 file changed

+71
-47
lines changed

1 file changed

+71
-47
lines changed

spec/bounds_safety/interoperation.tex

Lines changed: 71 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -177,26 +177,20 @@ \subsection{Description of cast operators}
177177
will disallow these casts. We also plan to revisit casts between pointers to
178178
unrelated types.
179179

180-
The \dynamicboundscast\ operator is not strictly needed.
181-
Programmers could write checks by hand. However, programmers
182-
would have to compute the bounds of expressions by hand too, so it is convenient to
183-
have it. It takes 1 to 3 arguments, depending on the kind of conversion being done:
180+
The \dynamicboundscast\ operator takes a type argument \var{D} and 1 or 2 expressions
181+
as arguments:
184182
\begin{itemize}
185183
\item
186-
\dynamicboundscastinst{\var{T}}{(\var{e1})}
187-
converts \var{e1} to either a \ptr\ or * type.
188-
\item
189-
\dynamicboundscastinst{\var{T}}{(\var{e1},
190-
\var{e2})} converts \var{e1} to an \arrayptr\ type with bounds \boundscount{\var{e2}}.
184+
\dynamicboundscastinst{\var{D}}{(\var{e1})}
185+
converts \var{e1} to either a \ptr\ or \code{*} type. \var{D} is the target \ptr\
186+
or \code{*} type. \var{D} cannot be a function pointer type.
191187
\item
192-
\dynamicboundscastinst{\var{T}}{(\var{e1},\var{e2},\var{e3})} converts \var{e1} to an
193-
\arrayptr\ type with bounds
194-
\bounds{\var{e2}}{\var{e3}}. \dynamicboundscastinst{\var{T}, \var{A}}
195-
{(\var{e1},\var{e2},\var{e3})}
196-
optionally changes the relative alignment of the bounds to \var{A}.
188+
\dynamicboundscastinst{\var{D}}{(\var{e1},
189+
\var{bounds-exp})} converts \var{e1} to an \arrayptr\ type with
190+
bounds \var{bounds-exp}. \var{D} is the target \arrayptr\ type.
197191
\end{itemize}
198192

199-
Here is the checking that is done. The bounds of \var{e1} are computed
193+
The bounds of \var{e1} are computed
200194
using the rules in Section~\ref{section:checking-nested-assignment-expressions}.
201195
If the bounds of \var{e1} are \boundsunknown, it is a compile-time error.
202196
If the bounds of \var{e1} are \boundsany, no runtime checks are needed.
@@ -208,46 +202,40 @@ \subsection{Description of cast operators}
208202
\item Otherwise, if the operator has the form:
209203
\begin{itemize}
210204
\item
211-
\dynamicboundscastinst{\var{T}}{(\var{e1})}:
212-
check that there is room for least one element of \var{T} by doing
213-
the check for \dynamicboundscastinst{\var{T}}{(e1, 1)}.
214-
\item
215-
\dynamicboundscastinst{\var{T}}{(\var{e1}, \var{e2})}:
216-
check that \var{lb}
217-
\code{<=} \var{e1} \code{&&} \var{e1} \code{+}
218-
\sizeof{referent-type(\var{T})} * e2 \code{<=}
219-
\var{ub}.
205+
\dynamicboundscastinst{\var{D}}{(\var{e1})}: \var{D} is pointer to some type \var{T}.
206+
Check that there is room for at least one element of \var{T} by doing
207+
the check for \dynamicboundscastinst{\arrayptrinst{\var{T}}}{(e1, \boundscount{\code{1}})}
220208
\item
221-
\dynamicboundscastinst{\var{T}}{(\var{e1},\var{e2},\var{e3})}:
222-
check that \var{lb} \code{<=} \var{e2} \code{&&}
223-
\var{e3} \code{<=} \var{ub}. Also check that \var{e1}, \var{e2}, and \var{e3}
224-
are relatively aligned to \var{T} (or the optional alignment parameter instead, if one
225-
is given).
226-
209+
\dynamicboundscastinst{\var{D}}{(\var{e1}, \var{bounds-exp})}:
210+
Expand \var{bounds-exp} to the standard
211+
form \boundsrelval{\var{target-lb}}{\var{target-ub}}{\var{v}}.
212+
Check that \var{lb} \code{<=} \var{target-lb} \code{&&} \var{target-ub} \code{<=} \var{ub}.
213+
Also check that \var{e1}, \var{target-lb}, and \var{target-lb} are relatively aligned
214+
with respect to\var{v}.
227215
\end{itemize}
228216
\end{itemize}
217+
The \dynamicboundscast\ operator is not strictly needed.
218+
Programmers could write checks by hand. It is convenient to have, though,
219+
because it avoids programmers having to write down the bounds of the source expression.
229220

230-
The operator \assumeboundscastinst{\var{T}}{} declares bounds that are trusted
221+
The operator \assumeboundscastinst{\var{D}}{} declares bounds that are trusted
231222
without verification:
232223
\begin{itemize}
233224
\item
234-
\assumeboundscastinst{\var{T}}{(\var{e1},\var{e2})}
235-
converts \var{e1} to an \arrayptr\ type with \boundscount{\var{e2}}
236-
\item
237-
\assumeboundscastinst{\var{T}}{(\var{e1},\var{e2},\var{e3})}
238-
converts \var{e1} to an
239-
\arrayptr\ type with \bounds{\var{e2}}{\var{e3}}.
240-
It must be statically provable that \var{e1}, \var{e2} and \var{e3}
241-
are relatively aligned for \var{T} (or the optional
242-
relative alignment parameter instead, if there is one).
225+
\assumeboundscastinst{\var{D}}{(\var{e1})}
226+
converts \var{e1} to a \ptr\ or \code{*} type. \var{D} is the target \ptr\ or \code{*} type.
243227
\item
244-
\assumeboundscastinst{\var{T}}{(\var{e1})}
245-
converts \var{e1} to a \ptr\ or \code{*} type.
228+
\assumeboundscastinst{\var{D}}{(\var{e1}, \var{bounds-exp})}
229+
converts \var{e1} to an \arrayptr\ type with bounds \var{bounds-exp}. \var{D} is the
230+
target \arrayptr\ type.
246231
\end{itemize}
232+
For the second form, the relative alignment must be statically
233+
provable: expand \var{bounds-exp} to the standard form
234+
\boundsrelval{\var{target-lb}}{\var{target-ub}}{\var{v}} and check
235+
that \var{e1}, \var{target-lb} and \var{target-ub} are relatively aligned to \var{v}.
247236

248-
If any rule depends on \sizeof{\var{T}} and \var{T} is
249-
an incomplete type, the cast operation that uses the rule shall fail
250-
to check at compile-time.
237+
If any rule requires knowing the size of an incomplete type,
238+
the cast operation that uses the rule shall fail to check at compile-time.
251239

252240
A subtle point about the C cast operator and \dynamicboundscast\
253241
are that they allow {\em bounds-safe casts} of an expression
@@ -262,7 +250,7 @@ \subsection{Examples}
262250

263251
Here are examples of uses of C cast operators to cast unchecked
264252
pointers to checked pointers. The static checking rules are straightforward
265-
to apply here. The examples assume that integers are
253+
to apply. The examples assume that integers are
266254
4 bytes in size:
267255
\begin{lstlisting}
268256
int x = 0;
@@ -271,7 +259,7 @@ \subsection{Examples}
271259
array_ptr<char> odd_pax : count(3) = (array_ptr<char>) px;
272260

273261
char data[12];
274-
ptr<int> pfirst = (ptr<int>) data; // pointed to 1st element as an integer;
262+
ptr<int> pfirst = (ptr<int>) data; // pointed to 1st element as an integer
275263
array_ptr<int> pdata : count(3) = (array_ptr<int>) data;
276264

277265
void swizzle(ptr<int> p) {
@@ -280,7 +268,43 @@ \subsection{Examples}
280268
bytes[0] = t3, bytes[1] = t2, bytes[2] = t1, bytes[3] = t0;
281269
}
282270
\end{lstlisting}
271+
The \dynamicboundscast\ operator is used typically when converting
272+
code to Checked C. The existing code may make assumptions
273+
about buffers being large enough that are potentially wrong.
274+
It would be better to check for errors directly, but the code may not
275+
be structured to handle errors:
276+
\begin{lstlisting}
277+
void f(array_ptr<char> buf : count(len), int len) {
278+
// We expect buf to have enough space for at least 12 integers.
279+
array_ptr<int> intbuf : count(12) =
280+
dynamic_bounds_cast<array_ptr<int>>(buf, count(12));
281+
...
282+
}
283283

284+
extern void copy(array_ptr<char> dest : count(n),
285+
array_ptr<char> src : count(n),
286+
size_t n);
287+
288+
void fill_buffer(array_ptr<char> dest : count(destlen),
289+
size_t destlen,
290+
array_ptr<char> src : count(srclen),
291+
size_t srclen) {
292+
// Existing code just assumed that dest was large enough; let's add a
293+
// dynamic_bounds_cast to be sure.
294+
array_ptr<char> target : count(srclen) =
295+
dynamic_bounds_cast<array_ptr<char>>(dest, count(srclen));
296+
copy(target, src, srclen);
297+
}
298+
\end{lstlisting}
299+
Here are examples of using the \assumeboundscast\ operator:
300+
\begin{lstlisting}
301+
// Memory-mapped hardware location where an integer can be written.
302+
ptr<int> output_loc = assume_bounds_cast<ptr<int>>(0x5055);
303+
// Memory-mapped hardware buffer starting at 0x6000 that stores 128
304+
// integers.
305+
array_ptr<int> output_buf : count(128) =
306+
assume_bounds_cast<array_ptr<int>>(0x6000, count(128));
307+
\end{lstlisting}
284308
\subsection{Non-examples}
285309

286310
Here are examples of incorrect uses of C cast operators to cast unchecked

0 commit comments

Comments
 (0)