summaryrefslogtreecommitdiffstats
path: root/security/sandbox/chromium/base/strings/safe_sprintf.h
blob: 01d649d07a81d0d9eae4c349e81c4e6c11238633 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_STRINGS_SAFE_SPRINTF_H_
#define BASE_STRINGS_SAFE_SPRINTF_H_

#include "build/build_config.h"

#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#if defined(OS_POSIX) || defined(OS_FUCHSIA)
// For ssize_t
#include <unistd.h>
#endif

#include "base/base_export.h"

namespace base {
namespace strings {

#if defined(COMPILER_MSVC)
// Define ssize_t inside of our namespace.
#if defined(_WIN64)
typedef __int64 ssize_t;
#else
typedef long ssize_t;
#endif
#endif

// SafeSPrintf() is a type-safe and completely self-contained version of
// snprintf().
//
// SafeSNPrintf() is an alternative function signature that can be used when
// not dealing with fixed-sized buffers. When possible, SafeSPrintf() should
// always be used instead of SafeSNPrintf()
//
// These functions allow for formatting complicated messages from contexts that
// require strict async-signal-safety. In fact, it is safe to call them from
// any low-level execution context, as they are guaranteed to make no library
// or system calls. It deliberately never touches "errno", either.
//
// The only exception to this rule is that in debug builds the code calls
// RAW_CHECK() to help diagnose problems when the format string does not
// match the rest of the arguments. In release builds, no CHECK()s are used,
// and SafeSPrintf() instead returns an output string that expands only
// those arguments that match their format characters. Mismatched arguments
// are ignored.
//
// The code currently only supports a subset of format characters:
//   %c, %o, %d, %x, %X, %p, and %s.
//
// SafeSPrintf() aims to be as liberal as reasonably possible. Integer-like
// values of arbitrary width can be passed to all of the format characters
// that expect integers. Thus, it is explicitly legal to pass an "int" to
// "%c", and output will automatically look at the LSB only. It is also
// explicitly legal to pass either signed or unsigned values, and the format
// characters will automatically interpret the arguments accordingly.
//
// It is still not legal to mix-and-match integer-like values with pointer
// values. For instance, you cannot pass a pointer to %x, nor can you pass an
// integer to %p.
//
// The one exception is "0" zero being accepted by "%p". This works-around
// the problem of C++ defining NULL as an integer-like value.
//
// All format characters take an optional width parameter. This must be a
// positive integer. For %d, %o, %x, %X and %p, if the width starts with
// a leading '0', padding is done with '0' instead of ' ' characters.
//
// There are a few features of snprintf()-style format strings, that
// SafeSPrintf() does not support at this time.
//
// If an actual user showed up, there is no particularly strong reason they
// couldn't be added. But that assumes that the trade-offs between complexity
// and utility are favorable.
//
// For example, adding support for negative padding widths, and for %n are all
// likely to be viewed positively. They are all clearly useful, low-risk, easy
// to test, don't jeopardize the async-signal-safety of the code, and overall
// have little impact on other parts of SafeSPrintf() function.
//
// On the other hands, adding support for alternate forms, positional
// arguments, grouping, wide characters, localization or floating point numbers
// are all unlikely to ever be added.
//
// SafeSPrintf() and SafeSNPrintf() mimic the behavior of snprintf() and they
// return the number of bytes needed to store the untruncated output. This
// does *not* include the terminating NUL byte.
//
// They return -1, iff a fatal error happened. This typically can only happen,
// if the buffer size is a) negative, or b) zero (i.e. not even the NUL byte
// can be written). The return value can never be larger than SSIZE_MAX-1.
// This ensures that the caller can always add one to the signed return code
// in order to determine the amount of storage that needs to be allocated.
//
// While the code supports type checking and while it is generally very careful
// to avoid printing incorrect values, it tends to be conservative in printing
// as much as possible, even when given incorrect parameters. Typically, in
// case of an error, the format string will not be expanded. (i.e. something
// like SafeSPrintf(buf, "%p %d", 1, 2) results in "%p 2"). See above for
// the use of RAW_CHECK() in debug builds, though.
//
// Basic example:
//   char buf[20];
//   base::strings::SafeSPrintf(buf, "The answer: %2d", 42);
//
// Example with dynamically sized buffer (async-signal-safe). This code won't
// work on Visual studio, as it requires dynamically allocating arrays on the
// stack. Consider picking a smaller value for |kMaxSize| if stack size is
// limited and known. On the other hand, if the parameters to SafeSNPrintf()
// are trusted and not controllable by the user, you can consider eliminating
// the check for |kMaxSize| altogether. The current value of SSIZE_MAX is
// essentially a no-op that just illustrates how to implement an upper bound:
//   const size_t kInitialSize = 128;
//   const size_t kMaxSize = std::numeric_limits<ssize_t>::max();
//   size_t size = kInitialSize;
//   for (;;) {
//     char buf[size];
//     size = SafeSNPrintf(buf, size, "Error message \"%s\"\n", err) + 1;
//     if (sizeof(buf) < kMaxSize && size > kMaxSize) {
//       size = kMaxSize;
//       continue;
//     } else if (size > sizeof(buf))
//       continue;
//     write(2, buf, size-1);
//     break;
//   }

namespace internal {
// Helpers that use C++ overloading, templates, and specializations to deduce
// and record type information from function arguments. This allows us to
// later write a type-safe version of snprintf().

struct Arg {
  enum Type { INT, UINT, STRING, POINTER };

  // Any integer-like value.
  Arg(signed char c) : type(INT) {
    integer.i = c;
    integer.width = sizeof(char);
  }
  Arg(unsigned char c) : type(UINT) {
    integer.i = c;
    integer.width = sizeof(char);
  }
  Arg(signed short j) : type(INT) {
    integer.i = j;
    integer.width = sizeof(short);
  }
  Arg(unsigned short j) : type(UINT) {
    integer.i = j;
    integer.width = sizeof(short);
  }
  Arg(signed int j) : type(INT) {
    integer.i = j;
    integer.width = sizeof(int);
  }
  Arg(unsigned int j) : type(UINT) {
    integer.i = j;
    integer.width = sizeof(int);
  }
  Arg(signed long j) : type(INT) {
    integer.i = j;
    integer.width = sizeof(long);
  }
  Arg(unsigned long j) : type(UINT) {
    integer.i = j;
    integer.width = sizeof(long);
  }
  Arg(signed long long j) : type(INT) {
    integer.i = j;
    integer.width = sizeof(long long);
  }
  Arg(unsigned long long j) : type(UINT) {
    integer.i = j;
    integer.width = sizeof(long long);
  }

  // A C-style text string.
  Arg(const char* s) : str(s), type(STRING) { }
  Arg(char* s)       : str(s), type(STRING) { }

  // Any pointer value that can be cast to a "void*".
  template<class T> Arg(T* p) : ptr((void*)p), type(POINTER) { }

  union {
    // An integer-like value.
    struct {
      int64_t       i;
      unsigned char width;
    } integer;

    // A C-style text string.
    const char* str;

    // A pointer to an arbitrary object.
    const void* ptr;
  };
  const enum Type type;
};

// This is the internal function that performs the actual formatting of
// an snprintf()-style format string.
BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t sz, const char* fmt,
                                 const Arg* args, size_t max_args);

#if !defined(NDEBUG)
// In debug builds, allow unit tests to artificially lower the kSSizeMax
// constant that is used as a hard upper-bound for all buffers. In normal
// use, this constant should always be std::numeric_limits<ssize_t>::max().
BASE_EXPORT void SetSafeSPrintfSSizeMaxForTest(size_t max);
BASE_EXPORT size_t GetSafeSPrintfSSizeMaxForTest();
#endif

}  // namespace internal

template<typename... Args>
ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt, Args... args) {
  // Use Arg() object to record type information and then copy arguments to an
  // array to make it easier to iterate over them.
  const internal::Arg arg_array[] = { args... };
  return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args));
}

template<size_t N, typename... Args>
ssize_t SafeSPrintf(char (&buf)[N], const char* fmt, Args... args) {
  // Use Arg() object to record type information and then copy arguments to an
  // array to make it easier to iterate over them.
  const internal::Arg arg_array[] = { args... };
  return internal::SafeSNPrintf(buf, N, fmt, arg_array, sizeof...(args));
}

// Fast-path when we don't actually need to substitute any arguments.
BASE_EXPORT ssize_t SafeSNPrintf(char* buf, size_t N, const char* fmt);
template<size_t N>
inline ssize_t SafeSPrintf(char (&buf)[N], const char* fmt) {
  return SafeSNPrintf(buf, N, fmt);
}

}  // namespace strings
}  // namespace base

#endif  // BASE_STRINGS_SAFE_SPRINTF_H_