Coverage Report

Created: 2026-05-06 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/tor/src/lib/log/escape.c
Line
Count
Source
1
/* Copyright (c) 2003, Roger Dingledine
2
 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3
 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4
/* See LICENSE for licensing information */
5
6
/**
7
 * \file escape.c
8
 * \brief Escape untrusted strings before sending them to the log.
9
 **/
10
11
#include "lib/log/escape.h"
12
#include "lib/log/util_bug.h"
13
#include "lib/string/compat_ctype.h"
14
#include "lib/string/printf.h"
15
#include "lib/malloc/malloc.h"
16
17
/** Allocate and return a new string representing the contents of <b>s</b>,
18
 * surrounded by quotes and using standard C escapes.
19
 *
20
 * Generally, we use this for logging values that come in over the network to
21
 * keep them from tricking users, and for sending certain values to the
22
 * controller.
23
 *
24
 * We trust values from the resolver, OS, configuration file, and command line
25
 * to not be maliciously ill-formed.  We validate incoming routerdescs and
26
 * SOCKS requests and addresses from BEGIN cells as they're parsed;
27
 * afterwards, we trust them as non-malicious.
28
 */
29
char *
30
esc_for_log(const char *s)
31
4.90M
{
32
4.90M
  const char *cp;
33
4.90M
  char *result, *outp;
34
4.90M
  size_t len = 3;
35
4.90M
  if (!s) {
36
0
    return tor_strdup("(null)");
37
0
  }
38
39
43.8M
  for (cp = s; *cp; ++cp) {
40
38.9M
    switch (*cp) {
41
4.75k
      case '\\':
42
22.4k
      case '\"':
43
95.0k
      case '\'':
44
107k
      case '\r':
45
111k
      case '\n':
46
119k
      case '\t':
47
119k
        len += 2;
48
119k
        break;
49
38.8M
      default:
50
38.8M
        if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127)
51
9.35M
          ++len;
52
29.4M
        else
53
29.4M
          len += 4;
54
38.8M
        break;
55
38.9M
    }
56
38.9M
  }
57
58
4.90M
  tor_assert(len <= SSIZE_MAX);
59
60
4.90M
  result = outp = tor_malloc(len);
61
4.90M
  *outp++ = '\"';
62
43.8M
  for (cp = s; *cp; ++cp) {
63
    /* This assertion should always succeed, since we will write at least
64
     * one char here, and two chars for closing quote and nul later */
65
38.9M
    tor_assert((outp-result) < (ssize_t)len-2);
66
38.9M
    switch (*cp) {
67
4.75k
      case '\\':
68
22.4k
      case '\"':
69
95.0k
      case '\'':
70
95.0k
        *outp++ = '\\';
71
95.0k
        *outp++ = *cp;
72
95.0k
        break;
73
3.53k
      case '\n':
74
3.53k
        *outp++ = '\\';
75
3.53k
        *outp++ = 'n';
76
3.53k
        break;
77
8.42k
      case '\t':
78
8.42k
        *outp++ = '\\';
79
8.42k
        *outp++ = 't';
80
8.42k
        break;
81
12.6k
      case '\r':
82
12.6k
        *outp++ = '\\';
83
12.6k
        *outp++ = 'r';
84
12.6k
        break;
85
38.8M
      default:
86
38.8M
        if (TOR_ISPRINT(*cp) && ((uint8_t)*cp)<127) {
87
9.35M
          *outp++ = *cp;
88
29.4M
        } else {
89
29.4M
          tor_assert((outp-result) < (ssize_t)len-4);
90
29.4M
          tor_snprintf(outp, 5, "\\%03o", (int)(uint8_t) *cp);
91
29.4M
          outp += 4;
92
29.4M
        }
93
38.8M
        break;
94
38.9M
    }
95
38.9M
  }
96
97
4.90M
  tor_assert((outp-result) <= (ssize_t)len-2);
98
4.90M
  *outp++ = '\"';
99
4.90M
  *outp++ = 0;
100
101
4.90M
  return result;
102
4.90M
}
103
104
/** Similar to esc_for_log. Allocate and return a new string representing
105
 * the first n characters in <b>chars</b>, surround by quotes and using
106
 * standard C escapes. If a NUL character is encountered in <b>chars</b>,
107
 * the resulting string will be terminated there.
108
 */
109
char *
110
esc_for_log_len(const char *chars, size_t n)
111
0
{
112
0
  char *string = tor_strndup(chars, n);
113
0
  char *string_escaped = esc_for_log(string);
114
0
  tor_free(string);
115
0
  return string_escaped;
116
0
}
117
118
/** Allocate and return a new string representing the contents of <b>s</b>,
119
 * surrounded by quotes and using standard C escapes.
120
 *
121
 * THIS FUNCTION IS NOT REENTRANT.  Don't call it from outside the main
122
 * thread.  Also, each call invalidates the last-returned value, so don't
123
 * try log_warn(LD_GENERAL, "%s %s", escaped(a), escaped(b));
124
 */
125
const char *
126
escaped(const char *s)
127
717k
{
128
717k
  static char *escaped_val_ = NULL;
129
717k
  tor_free(escaped_val_);
130
131
717k
  if (s)
132
700k
    escaped_val_ = esc_for_log(s);
133
16.9k
  else
134
16.9k
    escaped_val_ = NULL;
135
136
717k
  return escaped_val_;
137
717k
}