1 : /*
2 : * Copyright 2010 The Native Client Authors. All rights reserved.
3 : * Use of this source code is governed by a BSD-style license that can
4 : * be found in the LICENSE file.
5 : */
6 :
7 : //
8 : // checked_cast.h
9 : //
10 : // A template system, intended to be a drop-in replacement for static_cast<>,
11 : // which performs compile-time and (if necessary) runtime evaluation of
12 : // integral type casts to detect and handle arithmetic overflow errors.
13 : //
14 : #ifndef NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_
15 : #define NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_ 1
16 :
17 : // Windows defines std::min and std::max in a different header
18 : // than gcc.
19 : #if NACL_WINDOWS
20 : #include <xutility>
21 : #endif
22 :
23 : #include <limits>
24 :
25 : #if !NACL_WINDOWS
26 : // this is where std::min/max SHOULD live. It's included here (rather than
27 : // in an else block along with the Windows include above) to avoid breaking
28 : // google cpplint's header file inclusion order rules.
29 : #include <algorithm>
30 : #endif
31 :
32 : // TODO(ilewis): remove reference to base as soon as we can get COMPILE_ASSERT
33 : // from another source.
34 : #include "native_client/src/shared/platform/nacl_log.h"
35 :
36 : namespace nacl {
37 :
38 : //
39 : // Function to determine whether an information-preserving cast
40 : // is possible. In other words, if this function returns true, then
41 : // the input value has an exact representation in the target type.
42 : //
43 : template<typename target_t, typename source_t>
44 : bool can_cast(const source_t& input);
45 :
46 : //
47 : // Function to safely cast from one type to another, with a customizable
48 : // policy determining what happens if the cast cannot complete without
49 : // losing information.
50 : //
51 : template <
52 : typename target_t,
53 : typename source_t,
54 : template<typename, typename> class trunc_policy>
55 : target_t checked_cast(const source_t& input);
56 :
57 : //
58 : // Convenience wrappers for specializations of checked_cast with
59 : // different truncation policies
60 : //
61 : template<typename T, typename S>
62 : T assert_cast(const S& input);
63 :
64 : template<typename T, typename S>
65 : T saturate_cast(const S& input);
66 :
67 :
68 : //
69 : // Helper function prototypes
70 : //
71 : //
72 : namespace CheckedCast {
73 : //
74 : // OnTruncate* functions: policy functions that define
75 : // what to do if a checked cast can't be made without
76 : // truncation that loses data.
77 : //
78 : template <typename target_t, typename source_t>
79 : struct TruncationPolicySaturate {
80 : static target_t OnTruncate(const source_t& input);
81 : };
82 :
83 : template <typename target_t, typename source_t>
84 : struct TruncationPolicyAbort {
85 : static target_t OnTruncate(const source_t& input);
86 : };
87 :
88 : namespace detail {
89 : //-----------------------------------------------------------------------------
90 : // RuntimeHelpers
91 : //
92 : // Ugly nested templates--necessary because GCC is ever vigilant about type
93 : // safety, and has no way to temporarily disable certain warnings
94 : //
95 : //-----------------------------------------------------------------------------
96 :
97 : //
98 : // TrivialityChecker template
99 : // Makes decisions about whether a cast is trivial (does not require
100 : // the value of the input to be checked).
101 : //
102 : // The specializations are necessary to avoid running comparisons that
103 : // are either invalid or always true, since these comparisons trigger
104 : // warnings.
105 : //
106 : template<typename tlimits, typename slimits, bool IsCompileTimeTrivial>
107 : struct TrivialityChecker {
108 214 : static bool IsRuntimeTrivial() { return false; }
109 : };
110 :
111 : template<typename tlimits, typename slimits>
112 : struct TrivialityChecker<tlimits, slimits, true> {
113 3 : static bool IsRuntimeTrivial() {
114 : // Split into variables to bypass const value warning
115 3 : bool tlimits_lequ_min_slimits = tlimits::min() <= slimits::min();
116 3 : bool tlimits_gequ_max_slimits = tlimits::max() >= slimits::max();
117 3 : bool trivial = tlimits_lequ_min_slimits && tlimits_gequ_max_slimits;
118 3 : return trivial;
119 : }
120 : };
121 :
122 : //
123 : // Casting versions of std::min and std::max.
124 : // These should only be called from code that has checked to make
125 : // sure the cast is valid, otherwise compiler warnings will be
126 : // triggered.
127 : //
128 : template<typename A, typename B>
129 668 : static B cast_min(const A& a, const B& b) {
130 668 : return (static_cast<B>(a) < b) ? static_cast<B>(a) : b;
131 : }
132 :
133 : template<typename A, typename B>
134 21 : static B cast_max(const A& a, const B& b) {
135 21 : return (static_cast<B>(a) > b) ? static_cast<B>(a) : b;
136 : }
137 :
138 : //
139 : // Template specializations for determining valid ranges for
140 : // any given typecast. Specialized for different combinations
141 : // of signed/unsigned types.
142 : //
143 : template<typename target_t,
144 : typename source_t,
145 : bool SignednessDiffers,
146 : bool TargetIsSigned>
147 : struct RangeDetail {
148 : typedef std::numeric_limits<target_t> tlimits;
149 : typedef std::numeric_limits<source_t> slimits;
150 :
151 21 : static source_t OverlapMin() {
152 21 : return cast_max(tlimits::min(), slimits::min());
153 : }
154 24 : static source_t OverlapMax() {
155 24 : return cast_min(tlimits::max(), slimits::max());
156 : }
157 : };
158 :
159 : // signed target / unsigned source
160 : template<typename target_t, typename source_t>
161 : struct RangeDetail<target_t, source_t, true, true> {
162 : typedef std::numeric_limits<target_t> tlimits;
163 : typedef std::numeric_limits<source_t> slimits;
164 :
165 625 : static source_t OverlapMin() {
166 625 : if (tlimits::min() >= 0) {
167 0 : return cast_max(tlimits::min(), slimits::min());
168 : } else {
169 625 : return slimits::min();
170 : }
171 : }
172 628 : static source_t OverlapMax() {
173 628 : if (tlimits::max() >= 0) {
174 628 : return cast_min(tlimits::max(), slimits::max());
175 : } else {
176 0 : return slimits::min();
177 : }
178 : }
179 : };
180 :
181 : // unsigned target / signed source
182 : template<typename target_t, typename source_t>
183 : struct RangeDetail<target_t, source_t, true, false> {
184 : typedef std::numeric_limits<target_t> tlimits;
185 : typedef std::numeric_limits<source_t> slimits;
186 :
187 8 : static source_t OverlapMin() {
188 8 : if (slimits::min() >= 0) {
189 0 : return cast_max(tlimits::min(), slimits::min());
190 8 : } else if (slimits::max() >= 0) {
191 8 : return cast_min(tlimits::min(), slimits::max());
192 : } else {
193 0 : return slimits::min();
194 : }
195 : }
196 8 : static source_t OverlapMax() {
197 8 : if (slimits::max() >= 0) {
198 8 : return cast_min(tlimits::max(), slimits::max());
199 : } else {
200 0 : return slimits::min();
201 : }
202 : }
203 : };
204 :
205 : //
206 : // Wrapper for RangeDetail. Prevents RangeDetail objects
207 : // from being instantiated for unsupported types.
208 : //
209 : template<typename target_t, typename source_t, bool IsSupported>
210 : struct RangeHelper {
211 : typedef std::numeric_limits<target_t> tlimits;
212 : typedef std::numeric_limits<source_t> slimits;
213 :
214 : static bool OverlapExists() { return false; }
215 :
216 : // The return values from OverlapMin() and OverlapMax() are
217 : // arbitrary if OverlapExists() returns false, so we'll
218 : // just return the minimum source value for both.
219 : static source_t OverlapMin() { return slimits::min(); }
220 : static source_t OverlapMax() { return slimits::min(); }
221 : };
222 :
223 : template<typename target_t, typename source_t>
224 : struct RangeHelper<target_t, source_t, true> {
225 : typedef std::numeric_limits<target_t> tlimits;
226 : typedef std::numeric_limits<source_t> slimits;
227 :
228 : typedef RangeDetail<target_t,
229 : source_t,
230 : (tlimits::is_signed != slimits::is_signed),
231 : tlimits::is_signed> detail_t;
232 :
233 438 : static bool OverlapExists() {
234 438 : return detail_t::OverlapMin() < detail_t::OverlapMax();
235 : }
236 216 : static source_t OverlapMin() { return detail_t::OverlapMin(); }
237 222 : static source_t OverlapMax() { return detail_t::OverlapMax(); }
238 : };
239 :
240 : //
241 : // CastInfo
242 : //
243 : // Maintains information about how to cast between
244 : // two types.
245 : //
246 : template<typename target_t, typename source_t>
247 : struct CastInfo {
248 : typedef std::numeric_limits<target_t> tlimits;
249 : typedef std::numeric_limits<source_t> slimits;
250 :
251 : static const bool kSupported = tlimits::is_specialized
252 : && slimits::is_specialized
253 : && tlimits::is_integer
254 : && slimits::is_integer
255 : && tlimits::is_bounded
256 : && slimits::is_bounded;
257 :
258 : static const bool kIdenticalSignedness =
259 : (tlimits::is_signed == slimits::is_signed);
260 :
261 : // "digits" in numeric_limits refers to binary
262 : // digits. (Decimal digits are stored in a field
263 : // called digits10.)
264 : static const bool kSufficientBits =
265 : (tlimits::digits >= slimits::digits);
266 :
267 : static const bool kCompileTimeTrivial = kSupported
268 : && kIdenticalSignedness
269 : && kSufficientBits;
270 :
271 : typedef TrivialityChecker<tlimits,
272 : slimits,
273 : kCompileTimeTrivial> trivial_t;
274 : typedef RangeHelper<target_t, source_t, kSupported> range_t;
275 :
276 : // Can the cast be done without runtime checks--i.e. are
277 : // all possible input values also valid output values?
278 217 : static bool RuntimeTrivial() {
279 217 : return trivial_t::IsRuntimeTrivial();
280 : }
281 :
282 : // Are there any valid input values for which a cast would succeed?
283 222 : static bool RuntimePossible() {
284 222 : return range_t::OverlapExists();
285 : }
286 :
287 : // Is the given source value a valid target value?
288 216 : static bool RuntimeRangeCheck(const source_t& src) {
289 : return (range_t::OverlapExists()
290 : && src <= range_t::OverlapMax()
291 216 : && src >= range_t::OverlapMin());
292 : }
293 :
294 : // Range of source values which are also valid target values.
295 6 : static source_t RuntimeRangeMin() { return range_t::OverlapMin(); }
296 6 : static source_t RuntimeRangeMax() { return range_t::OverlapMax(); }
297 : };
298 : } // namespace detail
299 : } // namespace CheckedCast
300 : } // namespace nacl
301 :
302 :
303 : //-----------------------------------------------------------------------------
304 : //
305 : // Implementation
306 : //
307 : //-----------------------------------------------------------------------------
308 :
309 : //-----------------------------------------------------------------------------
310 : // checked_cast(const source_t& input)
311 : // An augmented replacement for static_cast. Does range checking,
312 : // overflow validation. Includes a policy which allows the caller to
313 : // specify what action to take if it's determined that the cast
314 : // would lose data.
315 : //
316 : // Template parameters:
317 : // target_t: the type on the left hand side of the cast
318 : // source_t: the type on the right hand side of the cast
319 : // trunc_policy: type of a policy object that will be called
320 : // if the cast would result in a loss of data. The
321 : // type must implement a method named OnTruncate which
322 : // is compatible with the following signature:
323 : // target_t (target_t& output, const source_t& input).
324 : // It is the responsibility of this function to
325 : // convert the value of 'input' and store the result
326 : // of the conversion in out parameter 'output'. It is
327 : // permissible for the function to abort or log warnings
328 : // if that is the desired behavior.
329 : //
330 : // Function parameters:
331 : // input: the value to convert.
332 : //
333 : // usage:
334 : // // naive truncation handler for sample purposes ONLY!!
335 : // template <typename T, typename S>
336 : // void naive_trunc(T& o, const S& i) {
337 : // // this only gets called if checked_cast can't do a safe automatic
338 : // // conversion. In real code you'd want to do something cool like
339 : // // clamp the incoming value or abort the program. For this sample
340 : // // we just return a c-style cast, which makes the outcome roughly
341 : // // equivalent to static_cast<>.
342 : // o = (T)i;
343 : // }
344 : //
345 : // void main() {
346 : // uint64_t foo = 0xffff0000ffff0000;
347 : // uint32_t bar = checked_cast<uint32_t>(foo, naive_trunc);
348 : // }
349 : //
350 : template <
351 : typename target_t,
352 : typename source_t,
353 : template<typename, typename> class trunc_policy>
354 211 : target_t nacl::checked_cast(const source_t& input) {
355 : typedef CheckedCast::detail::CastInfo<target_t, source_t> info;
356 :
357 2 : target_t output;
358 :
359 : //
360 : // Runtime checks--these should compile out for all basic types
361 : //
362 211 : if (nacl::can_cast<target_t>(input)) {
363 205 : output = static_cast<target_t>(input);
364 : } else {
365 6 : output = trunc_policy<target_t, source_t>::OnTruncate(input);
366 : }
367 :
368 211 : return output;
369 : }
370 :
371 : //-----------------------------------------------------------------------------
372 : // can_cast(const source_t& input)
373 : // Returns true if checked_cast will return without invoking trunc_policy.
374 : //-----------------------------------------------------------------------------
375 : template <typename target_t, typename source_t>
376 217 : bool nacl::can_cast(const source_t& input) {
377 : typedef CheckedCast::detail::CastInfo<target_t, source_t> info;
378 :
379 : bool result;
380 :
381 : //
382 : // Runtime checks--these should compile out for all basic types
383 : //
384 217 : result = info::RuntimeTrivial()
385 : || (info::RuntimePossible()
386 : && info::RuntimeRangeCheck(input));
387 :
388 217 : return result;
389 : }
390 :
391 :
392 : //
393 : // Convenience wrappers for specializations of checked_cast
394 : //
395 :
396 : //-----------------------------------------------------------------------------
397 : // checked_cast_fatal(const S& input)
398 : // Calls checked_cast; on truncation, log error and abort
399 : //-----------------------------------------------------------------------------
400 : template<typename T, typename S>
401 205 : T nacl::assert_cast(const S& input) {
402 205 : return checked_cast<T, S, CheckedCast::TruncationPolicyAbort>(input);
403 : }
404 : //-----------------------------------------------------------------------------
405 : // saturate_cast(const S& input)
406 : // Calls checked_cast; on truncation, saturates the input to the minimum
407 : // or maximum of the output.
408 : //-----------------------------------------------------------------------------
409 : template<typename T, typename S>
410 6 : T nacl::saturate_cast(const S& input) {
411 : return
412 6 : checked_cast<T, S, CheckedCast::TruncationPolicySaturate>(input);
413 : }
414 :
415 : //-----------------------------------------------------------------------------
416 : // CheckedCastDetail::OnTruncationSaturate
417 : // Implements the Saturate truncation policy.
418 : //-----------------------------------------------------------------------------
419 : template <typename target_t, typename source_t>
420 : target_t nacl
421 : ::CheckedCast
422 : ::TruncationPolicySaturate<target_t, source_t>
423 6 : ::OnTruncate(const source_t& input) {
424 : typedef detail::CastInfo<target_t, source_t> info;
425 :
426 6 : source_t clamped = input;
427 6 : bool valid = info::RuntimePossible();
428 :
429 6 : if (!valid) {
430 0 : NaClLog(LOG_FATAL, "Checked cast: type ranges do not overlap");
431 : }
432 :
433 6 : clamped = std::max(clamped, info::RuntimeRangeMin());
434 6 : clamped = std::min(clamped, info::RuntimeRangeMax());
435 :
436 6 : target_t output = static_cast<target_t>(clamped);
437 :
438 6 : return output;
439 : }
440 :
441 :
442 :
443 : //-----------------------------------------------------------------------------
444 : // CheckedCastDetail::OnTruncationAbort
445 : // Implements the Abort truncation policy.
446 : //-----------------------------------------------------------------------------
447 : template <typename target_t, typename source_t>
448 : target_t nacl
449 : ::CheckedCast
450 : ::TruncationPolicyAbort<target_t, source_t>
451 0 : ::OnTruncate(const source_t&) {
452 0 : NaClLog(LOG_FATAL, "Arithmetic overflow");
453 :
454 : // Unreachable, assuming that LOG_FATAL really is fatal
455 0 : return 0;
456 : }
457 :
458 :
459 : #endif /* NATIVE_CLIENT_SRC_INCLUDE_CHECKED_CAST_H_ */
|