xref: /aosp_15_r20/external/federated-compute/fcp/base/bounds.h (revision 14675a029014e728ec732f129a32e299b2da0601)
1*14675a02SAndroid Build Coastguard Worker /*
2*14675a02SAndroid Build Coastguard Worker  * Copyright 2018 Google LLC
3*14675a02SAndroid Build Coastguard Worker  *
4*14675a02SAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*14675a02SAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*14675a02SAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*14675a02SAndroid Build Coastguard Worker  *
8*14675a02SAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*14675a02SAndroid Build Coastguard Worker  *
10*14675a02SAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*14675a02SAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*14675a02SAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*14675a02SAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*14675a02SAndroid Build Coastguard Worker  * limitations under the License.
15*14675a02SAndroid Build Coastguard Worker  */
16*14675a02SAndroid Build Coastguard Worker 
17*14675a02SAndroid Build Coastguard Worker /**
18*14675a02SAndroid Build Coastguard Worker  * This file defines safe operations related to bounds checking and size
19*14675a02SAndroid Build Coastguard Worker  * calculations - robust against overflow, etc.
20*14675a02SAndroid Build Coastguard Worker  */
21*14675a02SAndroid Build Coastguard Worker 
22*14675a02SAndroid Build Coastguard Worker #ifndef FCP_BASE_BOUNDS_H_
23*14675a02SAndroid Build Coastguard Worker #define FCP_BASE_BOUNDS_H_
24*14675a02SAndroid Build Coastguard Worker 
25*14675a02SAndroid Build Coastguard Worker #include <limits>
26*14675a02SAndroid Build Coastguard Worker #include <type_traits>
27*14675a02SAndroid Build Coastguard Worker 
28*14675a02SAndroid Build Coastguard Worker #include "fcp/base/monitoring.h"
29*14675a02SAndroid Build Coastguard Worker 
30*14675a02SAndroid Build Coastguard Worker namespace fcp {
31*14675a02SAndroid Build Coastguard Worker 
32*14675a02SAndroid Build Coastguard Worker /**
33*14675a02SAndroid Build Coastguard Worker  * Attempts to cast 'from' to 'To'. Returns true (and sets *to) if the cast is
34*14675a02SAndroid Build Coastguard Worker  * lossless; otherwise, returns false without modifying *to.
35*14675a02SAndroid Build Coastguard Worker  *
36*14675a02SAndroid Build Coastguard Worker  * Examples:
37*14675a02SAndroid Build Coastguard Worker  *   TryCastInteger<uint8_t, uint32_t>(1024, &to) => false
38*14675a02SAndroid Build Coastguard Worker  *     static_cast<uint8_t>(1024) would yield 0
39*14675a02SAndroid Build Coastguard Worker  *   TryCastInteger<uint8_t, uint32_t>(123, &to) => true
40*14675a02SAndroid Build Coastguard Worker  *   TryCastInteger<uint32_t, int32_t>(123, &to) => true
41*14675a02SAndroid Build Coastguard Worker  *   TryCastInteger<uint32_t, int32_t>(-1, &to) => false
42*14675a02SAndroid Build Coastguard Worker  */
43*14675a02SAndroid Build Coastguard Worker template <typename To, typename From>
TryCastInteger(From from,To * to)44*14675a02SAndroid Build Coastguard Worker bool TryCastInteger(From from, To* to) {
45*14675a02SAndroid Build Coastguard Worker   static_assert(std::is_integral<To>::value && std::is_integral<From>::value,
46*14675a02SAndroid Build Coastguard Worker                 "Both types must be integral");
47*14675a02SAndroid Build Coastguard Worker 
48*14675a02SAndroid Build Coastguard Worker   if (std::is_signed<From>::value == std::is_signed<To>::value) {
49*14675a02SAndroid Build Coastguard Worker     // Same sign: Easy!
50*14675a02SAndroid Build Coastguard Worker     if (from < std::numeric_limits<To>::min() ||
51*14675a02SAndroid Build Coastguard Worker         from > std::numeric_limits<To>::max()) {
52*14675a02SAndroid Build Coastguard Worker       return false;
53*14675a02SAndroid Build Coastguard Worker     }
54*14675a02SAndroid Build Coastguard Worker   } else if (std::is_signed<From>::value && !std::is_signed<To>::value) {
55*14675a02SAndroid Build Coastguard Worker     // Signed => Unsigned: Widening conversion would sign-extend 'from' first;
56*14675a02SAndroid Build Coastguard Worker     // i.e. -1 would look larger than To's min(). Negative values are definitely
57*14675a02SAndroid Build Coastguard Worker     // out of range anyway.  Positive values are effectively zero-extended,
58*14675a02SAndroid Build Coastguard Worker     // which is fine.
59*14675a02SAndroid Build Coastguard Worker     if (from < 0 || from > std::numeric_limits<To>::max()) {
60*14675a02SAndroid Build Coastguard Worker       return false;
61*14675a02SAndroid Build Coastguard Worker     }
62*14675a02SAndroid Build Coastguard Worker   } else {
63*14675a02SAndroid Build Coastguard Worker     // Unsigned => Signed: We don't want to mention min(), since widening
64*14675a02SAndroid Build Coastguard Worker     // conversion of min() would have the same problem as in the prior case.
65*14675a02SAndroid Build Coastguard Worker     if (from > std::numeric_limits<To>::max()) {
66*14675a02SAndroid Build Coastguard Worker       return false;
67*14675a02SAndroid Build Coastguard Worker     }
68*14675a02SAndroid Build Coastguard Worker   }
69*14675a02SAndroid Build Coastguard Worker 
70*14675a02SAndroid Build Coastguard Worker   *to = static_cast<To>(from);
71*14675a02SAndroid Build Coastguard Worker   return true;
72*14675a02SAndroid Build Coastguard Worker }
73*14675a02SAndroid Build Coastguard Worker 
74*14675a02SAndroid Build Coastguard Worker /**
75*14675a02SAndroid Build Coastguard Worker  * Casts from 'from' to 'To'. Check-fails if the cast is not lossless.
76*14675a02SAndroid Build Coastguard Worker  * See also: TryCastInteger
77*14675a02SAndroid Build Coastguard Worker  */
78*14675a02SAndroid Build Coastguard Worker template <typename To, typename From>
CastIntegerChecked(From from)79*14675a02SAndroid Build Coastguard Worker To CastIntegerChecked(From from) {
80*14675a02SAndroid Build Coastguard Worker   To to;
81*14675a02SAndroid Build Coastguard Worker   FCP_CHECK(TryCastInteger(from, &to));
82*14675a02SAndroid Build Coastguard Worker   return to;
83*14675a02SAndroid Build Coastguard Worker }
84*14675a02SAndroid Build Coastguard Worker 
85*14675a02SAndroid Build Coastguard Worker /** Multiplies without the possibility of overflow. */
SafeMultiply(uint32_t a,uint32_t b)86*14675a02SAndroid Build Coastguard Worker inline uint64_t SafeMultiply(uint32_t a, uint32_t b) {
87*14675a02SAndroid Build Coastguard Worker   return static_cast<uint64_t>(a) * static_cast<uint64_t>(b);
88*14675a02SAndroid Build Coastguard Worker }
89*14675a02SAndroid Build Coastguard Worker 
90*14675a02SAndroid Build Coastguard Worker /**
91*14675a02SAndroid Build Coastguard Worker  * Represents an embedded address space as a pair of a starting address and a
92*14675a02SAndroid Build Coastguard Worker  * size. This is a correspondence of the addresses [0, size) <=> [start, start +
93*14675a02SAndroid Build Coastguard Worker  * size), for the embedded address space and this one, respectively.
94*14675a02SAndroid Build Coastguard Worker  *
95*14675a02SAndroid Build Coastguard Worker  * A ForeignPointer represents an address in an embedded address space (left).
96*14675a02SAndroid Build Coastguard Worker  * Given a ForeignPointer and a supposed size, one can use
97*14675a02SAndroid Build Coastguard Worker  * MapOutOfAddressSpace() to get a pointer in this address space (right),
98*14675a02SAndroid Build Coastguard Worker  * subject to bounds checking.
99*14675a02SAndroid Build Coastguard Worker  *
100*14675a02SAndroid Build Coastguard Worker  * We require that start + size does not overflow; this is convenient for bounds
101*14675a02SAndroid Build Coastguard Worker  * checks. Since start + size is the open part of the interval (one past the
102*14675a02SAndroid Build Coastguard Worker  * end), that happens to mean that ~0 cannot be in bounds (irrelevant in
103*14675a02SAndroid Build Coastguard Worker  * practice).
104*14675a02SAndroid Build Coastguard Worker  */
105*14675a02SAndroid Build Coastguard Worker struct AddressSpace {
106*14675a02SAndroid Build Coastguard Worker   void* start;
107*14675a02SAndroid Build Coastguard Worker   uint64_t size;
108*14675a02SAndroid Build Coastguard Worker 
109*14675a02SAndroid Build Coastguard Worker   /**
110*14675a02SAndroid Build Coastguard Worker    * Returns a representation of the ambient, 'native' address space - it just
111*14675a02SAndroid Build Coastguard Worker    * starts at address zero and has maximum size. Note that the highest address
112*14675a02SAndroid Build Coastguard Worker    * (~0) is thus out of bounds.
113*14675a02SAndroid Build Coastguard Worker    */
CurrentAddressSpace114*14675a02SAndroid Build Coastguard Worker   static constexpr AddressSpace Current() {
115*14675a02SAndroid Build Coastguard Worker     return AddressSpace{nullptr, ~static_cast<uint64_t>(0)};
116*14675a02SAndroid Build Coastguard Worker   }
117*14675a02SAndroid Build Coastguard Worker 
118*14675a02SAndroid Build Coastguard Worker   /**
119*14675a02SAndroid Build Coastguard Worker    * Returns an AddressSpace spanning mapping [0, size) <=> [start, start +
120*14675a02SAndroid Build Coastguard Worker    * size).
121*14675a02SAndroid Build Coastguard Worker    */
EmbeddedAddressSpace122*14675a02SAndroid Build Coastguard Worker   static AddressSpace Embedded(void* start, uint64_t size) {
123*14675a02SAndroid Build Coastguard Worker     uint64_t end;
124*14675a02SAndroid Build Coastguard Worker     FCP_CHECK(
125*14675a02SAndroid Build Coastguard Worker         !__builtin_add_overflow(reinterpret_cast<uint64_t>(start), size, &end));
126*14675a02SAndroid Build Coastguard Worker     return AddressSpace{start, size};
127*14675a02SAndroid Build Coastguard Worker   }
128*14675a02SAndroid Build Coastguard Worker };
129*14675a02SAndroid Build Coastguard Worker 
130*14675a02SAndroid Build Coastguard Worker /**
131*14675a02SAndroid Build Coastguard Worker  * An address in some AddressSpace. It can be translated to a pointer in this
132*14675a02SAndroid Build Coastguard Worker  * address space with MapOutOfAddressSpace().
133*14675a02SAndroid Build Coastguard Worker  */
134*14675a02SAndroid Build Coastguard Worker struct ForeignPointer {
135*14675a02SAndroid Build Coastguard Worker   uint64_t value;
136*14675a02SAndroid Build Coastguard Worker };
137*14675a02SAndroid Build Coastguard Worker 
138*14675a02SAndroid Build Coastguard Worker /**
139*14675a02SAndroid Build Coastguard Worker  * Translates a ForeignPointer out of an embedded AddressSpace, yielding a void*
140*14675a02SAndroid Build Coastguard Worker  * in this address space. The pointer is understood to refer to (size * count)
141*14675a02SAndroid Build Coastguard Worker  * bytes of memory (i.e. an array), as useful pointers tend to do.
142*14675a02SAndroid Build Coastguard Worker  *
143*14675a02SAndroid Build Coastguard Worker  * If that span does _not_ fully reside within the provided AddressSpace,
144*14675a02SAndroid Build Coastguard Worker  * returns nullptr. Otherwise, returns space.start + ptr.value.
145*14675a02SAndroid Build Coastguard Worker  *
146*14675a02SAndroid Build Coastguard Worker  * This function is intended to behave safely for arbitrary values of 'ptr',
147*14675a02SAndroid Build Coastguard Worker  * 'size', and 'count', perhaps provided by untrusted code. 'size' and 'count'
148*14675a02SAndroid Build Coastguard Worker  * are provided separately for this reason (to save the caller from worrying
149*14675a02SAndroid Build Coastguard Worker  * about multiplication overflow).
150*14675a02SAndroid Build Coastguard Worker  */
MapOutOfAddressSpace(AddressSpace const & space,ForeignPointer ptr,uint32_t size,uint32_t count)151*14675a02SAndroid Build Coastguard Worker inline void* MapOutOfAddressSpace(AddressSpace const& space, ForeignPointer ptr,
152*14675a02SAndroid Build Coastguard Worker                                   uint32_t size, uint32_t count) {
153*14675a02SAndroid Build Coastguard Worker   // Because the element size and count are each 32 bits, we can't overflow a 64
154*14675a02SAndroid Build Coastguard Worker   // bit total_size.
155*14675a02SAndroid Build Coastguard Worker   uint64_t total_size = SafeMultiply(size, count);
156*14675a02SAndroid Build Coastguard Worker 
157*14675a02SAndroid Build Coastguard Worker   // The span in the embedded space is [ptr, ptr + total_size).
158*14675a02SAndroid Build Coastguard Worker   uint64_t ptr_end;
159*14675a02SAndroid Build Coastguard Worker   if (__builtin_add_overflow(ptr.value, total_size, &ptr_end)) {
160*14675a02SAndroid Build Coastguard Worker     return nullptr;
161*14675a02SAndroid Build Coastguard Worker   }
162*14675a02SAndroid Build Coastguard Worker 
163*14675a02SAndroid Build Coastguard Worker   // The embedded address space ranges from [0, space.size). We know that ptr >=
164*14675a02SAndroid Build Coastguard Worker   // 0 and that ptr <= ptr_end, so it is sufficient to check that ptr_end <=
165*14675a02SAndroid Build Coastguard Worker   // space.size. Note that this allows ptr == space.size, iff total_size == 0.
166*14675a02SAndroid Build Coastguard Worker   if (ptr_end > space.size) {
167*14675a02SAndroid Build Coastguard Worker     return nullptr;
168*14675a02SAndroid Build Coastguard Worker   }
169*14675a02SAndroid Build Coastguard Worker 
170*14675a02SAndroid Build Coastguard Worker   // AddressSpace requires that start + size does not overflow.
171*14675a02SAndroid Build Coastguard Worker   //  - Since ptr_end <= space.size, space.start + ptr_end does not overflow.
172*14675a02SAndroid Build Coastguard Worker   //  - Since ptr <= ptr_end, space.start + ptr does not overflow.
173*14675a02SAndroid Build Coastguard Worker   // Therefore, we can return the offset span space.start + [ptr, ptr_end).
174*14675a02SAndroid Build Coastguard Worker   return reinterpret_cast<void*>(reinterpret_cast<uint64_t>(space.start) +
175*14675a02SAndroid Build Coastguard Worker                                  ptr.value);
176*14675a02SAndroid Build Coastguard Worker }
177*14675a02SAndroid Build Coastguard Worker 
178*14675a02SAndroid Build Coastguard Worker }  // namespace fcp
179*14675a02SAndroid Build Coastguard Worker 
180*14675a02SAndroid Build Coastguard Worker #endif  // FCP_BASE_BOUNDS_H_
181