Bug Summary

File:build-scan/../src/getty-generator/getty-generator.c
Warning:line 179, column 17
Potential leak of memory pointed to by 'tty'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-unknown-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name getty-generator.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model static -mframe-pointer=all -relaxed-aliasing -menable-no-infs -menable-no-nans -menable-unsafe-fp-math -fno-signed-zeros -mreassociate -freciprocal-math -fdenormal-fp-math=preserve-sign,preserve-sign -ffp-contract=fast -fno-rounding-math -ffast-math -ffinite-math-only -mconstructor-aliases -munwind-tables -target-cpu x86-64 -tune-cpu generic -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/lib64/clang/12.0.0 -include config.h -I systemd-getty-generator.p -I . -I .. -I src/basic -I ../src/basic -I src/shared -I ../src/shared -I src/systemd -I ../src/systemd -I src/journal -I ../src/journal -I src/journal-remote -I ../src/journal-remote -I src/nspawn -I ../src/nspawn -I src/resolve -I ../src/resolve -I src/timesync -I ../src/timesync -I ../src/time-wait-sync -I src/login -I ../src/login -I src/udev -I ../src/udev -I src/libudev -I ../src/libudev -I src/core -I ../src/core -I ../src/libsystemd/sd-bus -I ../src/libsystemd/sd-device -I ../src/libsystemd/sd-hwdb -I ../src/libsystemd/sd-id128 -I ../src/libsystemd/sd-netlink -I ../src/libsystemd/sd-network -I src/libsystemd-network -I ../src/libsystemd-network -D _FILE_OFFSET_BITS=64 -internal-isystem /usr/local/include -internal-isystem /usr/lib64/clang/12.0.0/include -internal-externc-isystem /include -internal-externc-isystem /usr/include -Wwrite-strings -Wno-unused-parameter -Wno-missing-field-initializers -Wno-unused-result -Wno-format-signedness -Wno-error=nonnull -std=gnu99 -fconst-strings -fdebug-compilation-dir /home/mrc0mmand/repos/@redhat-plumbers/systemd-rhel8/build-scan -ferror-limit 19 -fvisibility hidden -stack-protector 2 -fgnuc-version=4.2.1 -fcolor-diagnostics -analyzer-output=html -faddrsig -o /tmp/scan-build-2021-07-16-221226-1465241-1 -x c ../src/getty-generator/getty-generator.c
1/* SPDX-License-Identifier: LGPL-2.1+ */
2
3#include <errno(*__errno_location ()).h>
4#include <fcntl.h>
5#include <string.h>
6#include <unistd.h>
7
8#include "alloc-util.h"
9#include "fd-util.h"
10#include "fileio.h"
11#include "log.h"
12#include "mkdir.h"
13#include "path-util.h"
14#include "process-util.h"
15#include "string-util.h"
16#include "terminal-util.h"
17#include "unit-name.h"
18#include "util.h"
19#include "virt.h"
20
21static const char *arg_dest = "/tmp";
22
23static int add_symlink(const char *fservice, const char *tservice) {
24 char *from, *to;
25 int r;
26
27 assert(fservice)do { if ((__builtin_expect(!!(!(fservice)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("fservice"), "../src/getty-generator/getty-generator.c"
, 27, __PRETTY_FUNCTION__); } while (0)
;
28 assert(tservice)do { if ((__builtin_expect(!!(!(tservice)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("tservice"), "../src/getty-generator/getty-generator.c"
, 28, __PRETTY_FUNCTION__); } while (0)
;
29
30 from = strjoina(SYSTEM_DATA_UNIT_PATH "/", fservice)({ const char *_appendees_[] = { "/usr/lib/systemd/system" "/"
, fservice }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for
(_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_
)/sizeof((_appendees_)[0]), ((void)0))) && _appendees_
[_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca
(_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr
( !__builtin_types_compatible_p(typeof(_appendees_), typeof(&
*(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0]
), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy
(_p_, _appendees_[_i_]); *_p_ = 0; _d_; })
;
31 to = strjoina(arg_dest, "/getty.target.wants/", tservice)({ const char *_appendees_[] = { arg_dest, "/getty.target.wants/"
, tservice }; char *_d_, *_p_; size_t _len_ = 0; size_t _i_; for
(_i_ = 0; _i_ < __extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_
)/sizeof((_appendees_)[0]), ((void)0))) && _appendees_
[_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca
(_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr
( !__builtin_types_compatible_p(typeof(_appendees_), typeof(&
*(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0]
), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy
(_p_, _appendees_[_i_]); *_p_ = 0; _d_; })
;
32
33 mkdir_parents_label(to, 0755);
34
35 r = symlink(from, to);
36 if (r < 0) {
37 /* In case console=hvc0 is passed this will very likely result in EEXIST */
38 if (errno(*__errno_location ()) == EEXIST17)
39 return 0;
40
41 return log_error_errno(errno, "Failed to create symlink %s: %m", to)({ int _level = ((3)), _e = (((*__errno_location ()))), _realm
= (LOG_REALM_SYSTEMD); (log_get_max_level_realm(_realm) >=
((_level) & 0x07)) ? log_internal_realm(((_realm) <<
10 | (_level)), _e, "../src/getty-generator/getty-generator.c"
, 41, __func__, "Failed to create symlink %s: %m", to) : -abs
(_e); })
;
42 }
43
44 return 0;
45}
46
47static int add_serial_getty(const char *tty) {
48 _cleanup_free___attribute__((cleanup(freep))) char *n = NULL((void*)0);
49 int r;
50
51 assert(tty)do { if ((__builtin_expect(!!(!(tty)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("tty"), "../src/getty-generator/getty-generator.c"
, 51, __PRETTY_FUNCTION__); } while (0)
;
52
53 log_debug("Automatically adding serial getty for /dev/%s.", tty)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/getty-generator/getty-generator.c", 53, __func__, "Automatically adding serial getty for /dev/%s."
, tty) : -abs(_e); })
;
54
55 r = unit_name_from_path_instance("serial-getty", tty, ".service", &n);
56 if (r < 0)
57 return log_error_errno(r, "Failed to generate service name: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/getty-generator/getty-generator.c", 57, __func__, "Failed to generate service name: %m"
) : -abs(_e); })
;
58
59 return add_symlink("serial-getty@.service", n);
60}
61
62static int add_container_getty(const char *tty) {
63 _cleanup_free___attribute__((cleanup(freep))) char *n = NULL((void*)0);
64 int r;
65
66 assert(tty)do { if ((__builtin_expect(!!(!(tty)),0))) log_assert_failed_realm
(LOG_REALM_SYSTEMD, ("tty"), "../src/getty-generator/getty-generator.c"
, 66, __PRETTY_FUNCTION__); } while (0)
;
67
68 log_debug("Automatically adding container getty for /dev/pts/%s.", tty)({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/getty-generator/getty-generator.c", 68, __func__, "Automatically adding container getty for /dev/pts/%s."
, tty) : -abs(_e); })
;
69
70 r = unit_name_from_path_instance("container-getty", tty, ".service", &n);
71 if (r < 0)
72 return log_error_errno(r, "Failed to generate service name: %m")({ int _level = ((3)), _e = ((r)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/getty-generator/getty-generator.c", 72, __func__, "Failed to generate service name: %m"
) : -abs(_e); })
;
73
74 return add_symlink("container-getty@.service", n);
75}
76
77static int verify_tty(const char *name) {
78 _cleanup_close___attribute__((cleanup(closep))) int fd = -1;
79 const char *p;
80
81 /* Some TTYs are weird and have been enumerated but don't work
82 * when you try to use them, such as classic ttyS0 and
83 * friends. Let's check that and open the device and run
84 * isatty() on it. */
85
86 p = strjoina("/dev/", name)({ const char *_appendees_[] = { "/dev/", name }; char *_d_, *
_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ < __extension__
(__builtin_choose_expr( !__builtin_types_compatible_p(typeof
(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_
)/sizeof((_appendees_)[0]), ((void)0))) && _appendees_
[_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca
(_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr
( !__builtin_types_compatible_p(typeof(_appendees_), typeof(&
*(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0]
), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy
(_p_, _appendees_[_i_]); *_p_ = 0; _d_; })
;
87
88 /* O_NONBLOCK is essential here, to make sure we don't wait
89 * for DCD */
90 fd = open(p, O_RDWR02|O_NONBLOCK04000|O_NOCTTY0400|O_CLOEXEC02000000|O_NOFOLLOW0400000);
91 if (fd < 0)
92 return -errno(*__errno_location ());
93
94 errno(*__errno_location ()) = 0;
95 if (isatty(fd) <= 0)
96 return errno(*__errno_location ()) > 0 ? -errno(*__errno_location ()) : -EIO5;
97
98 return 0;
99}
100
101int main(int argc, char *argv[]) {
102
103 static const char virtualization_consoles[] =
104 "hvc0\0"
105 "xvc0\0"
106 "hvsi0\0"
107 "sclp_line0\0"
108 "ttysclp0\0"
109 "3270!tty1\0";
110
111 _cleanup_free___attribute__((cleanup(freep))) char *active = NULL((void*)0);
112 const char *j;
113 int r;
114
115 if (argc > 1 && argc != 4) {
1
Assuming 'argc' is <= 1
116 log_error("This program takes three or no arguments.")({ int _level = (((3))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/getty-generator/getty-generator.c", 116, __func__, "This program takes three or no arguments."
) : -abs(_e); })
;
117 return EXIT_FAILURE1;
118 }
119
120 if (argc
1.1
'argc' is <= 1
> 1)
2
Taking false branch
121 arg_dest = argv[1];
122
123 log_set_prohibit_ipc(true1);
124 log_set_target(LOG_TARGET_AUTO);
125 log_parse_environment()log_parse_environment_realm(LOG_REALM_SYSTEMD);
126 log_open();
127
128 umask(0022);
129
130 if (detect_container() > 0) {
3
Assuming the condition is false
4
Taking false branch
131 _cleanup_free___attribute__((cleanup(freep))) char *container_ttys = NULL((void*)0);
132
133 log_debug("Automatically adding console shell.")({ int _level = (((7))), _e = ((0)), _realm = (LOG_REALM_SYSTEMD
); (log_get_max_level_realm(_realm) >= ((_level) & 0x07
)) ? log_internal_realm(((_realm) << 10 | (_level)), _e
, "../src/getty-generator/getty-generator.c", 133, __func__, "Automatically adding console shell."
) : -abs(_e); })
;
134
135 if (add_symlink("console-getty.service", "console-getty.service") < 0)
136 return EXIT_FAILURE1;
137
138 /* When $container_ttys is set for PID 1, spawn
139 * gettys on all ptys named therein. Note that despite
140 * the variable name we only support ptys here. */
141
142 r = getenv_for_pid(1, "container_ttys", &container_ttys);
143 if (r > 0) {
144 const char *word, *state;
145 size_t l;
146
147 FOREACH_WORD(word, l, container_ttys, state)for ((state) = (container_ttys), (word) = split(&(state),
&(l), (" \t\n\r"), (0)); (word); (word) = split(&(state
), &(l), (" \t\n\r"), (0)))
{
148 const char *t;
149 char tty[l + 1];
150
151 memcpy(tty, word, l);
152 tty[l] = 0;
153
154 /* First strip off /dev/ if it is specified */
155 t = path_startswith(tty, "/dev/");
156 if (!t)
157 t = tty;
158
159 /* Then, make sure it's actually a pty */
160 t = path_startswith(t, "pts/");
161 if (!t)
162 continue;
163
164 if (add_container_getty(t) < 0)
165 return EXIT_FAILURE1;
166 }
167 }
168
169 /* Don't add any further magic if we are in a container */
170 return EXIT_SUCCESS0;
171 }
172
173 if (read_one_line_file("/sys/class/tty/console/active", &active) >= 0) {
5
Assuming the condition is true
6
Taking true branch
174 const char *word, *state;
175 size_t l;
176
177 /* Automatically add in a serial getty on all active
178 * kernel consoles */
179 FOREACH_WORD(word, l, active, state)for ((state) = (active), (word) = split(&(state), &(l
), (" \t\n\r"), (0)); (word); (word) = split(&(state), &
(l), (" \t\n\r"), (0)))
{
7
Loop condition is true. Entering loop body
12
Potential leak of memory pointed to by 'tty'
180 _cleanup_free___attribute__((cleanup(freep))) char *tty = NULL((void*)0);
181
182 tty = strndup(word, l);
8
Memory is allocated
183 if (!tty) {
9
Assuming 'tty' is non-null
10
Taking false branch
184 log_oom()log_oom_internal(LOG_REALM_SYSTEMD, "../src/getty-generator/getty-generator.c"
, 184, __func__)
;
185 return EXIT_FAILURE1;
186 }
187
188 /* We assume that gettys on virtual terminals are
189 * started via manual configuration and do this magic
190 * only for non-VC terminals. */
191
192 if (isempty(tty) || tty_is_vc(tty))
193 continue;
11
Execution continues on line 179
194
195 if (verify_tty(tty) < 0)
196 continue;
197
198 if (add_serial_getty(tty) < 0)
199 return EXIT_FAILURE1;
200 }
201 }
202
203 /* Automatically add in a serial getty on the first
204 * virtualizer console */
205 NULSTR_FOREACH(j, virtualization_consoles)for ((j) = (virtualization_consoles); (j) && *(j); (j
) = strchr((j), 0)+1)
{
206 char *p;
207
208 p = strjoina("/sys/class/tty/", j)({ const char *_appendees_[] = { "/sys/class/tty/", j }; char
*_d_, *_p_; size_t _len_ = 0; size_t _i_; for (_i_ = 0; _i_ <
__extension__ (__builtin_choose_expr( !__builtin_types_compatible_p
(typeof(_appendees_), typeof(&*(_appendees_))), sizeof(_appendees_
)/sizeof((_appendees_)[0]), ((void)0))) && _appendees_
[_i_]; _i_++) _len_ += strlen(_appendees_[_i_]); _p_ = _d_ = __builtin_alloca
(_len_ + 1); for (_i_ = 0; _i_ < __extension__ (__builtin_choose_expr
( !__builtin_types_compatible_p(typeof(_appendees_), typeof(&
*(_appendees_))), sizeof(_appendees_)/sizeof((_appendees_)[0]
), ((void)0))) && _appendees_[_i_]; _i_++) _p_ = stpcpy
(_p_, _appendees_[_i_]); *_p_ = 0; _d_; })
;
209 if (access(p, F_OK0) < 0)
210 continue;
211
212 if (add_serial_getty(j) < 0)
213 return EXIT_FAILURE1;
214 }
215
216 return EXIT_SUCCESS0;
217}