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