diff --git a/0081-journal-refresh-cached-credentials-of-stdout-streams.patch b/0081-journal-refresh-cached-credentials-of-stdout-streams.patch new file mode 100644 index 0000000000000000000000000000000000000000..195dda4f1540bd9f7d1fbac24f32adc587bf56ac --- /dev/null +++ b/0081-journal-refresh-cached-credentials-of-stdout-streams.patch @@ -0,0 +1,148 @@ +From 09d0b46ab61bebafe5bdc1be95ee153dfb13d6bc Mon Sep 17 00:00:00 2001 +From: Lorenz Bauer +Date: Mon, 4 Nov 2019 16:35:46 +0000 +Subject: [PATCH] journal: refresh cached credentials of stdout streams + +journald assumes that getsockopt(SO_PEERCRED) correctly identifies the +process on the remote end of the socket. However, this is incorrect +according to man 7 socket: + +The returned credentials are those that were in effect at the + time of the call to connect(2) or socketpair(2). + +This becomes a problem when a new process inherits the stdout stream +from a parent. First, log messages from the child process will +be attributed to the parent. Second, the struct ucred used by journald +becomes invalid as soon as the parent exits. Further sendmsg calls then +fail with ENOENT. Logs for the child process then vanish from the journal. + +Fix this by using recvmsg on the stdout stream, and refreshing the cached +struct ucred if SCM_CREDENTIALS indicate a new process. + +Fixes #13708 +--- + src/journal/journald-stream.c | 49 ++++++++++++++++++++++++++++++++++-- + test/TEST-04-JOURNAL/test-journal.sh | 16 ++++++++++++ + 2 files changed, 63 insertions(+), 2 deletions(-) + +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index afebade..22a70ce 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -487,11 +487,22 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) { + } + + static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) { ++ uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + StdoutStream *s = userdata; ++ struct ucred *ucred = NULL; ++ struct cmsghdr *cmsg; ++ struct iovec iovec; + size_t limit; + ssize_t l; + int r; + ++ struct msghdr msghdr = { ++ .msg_iov = &iovec, ++ .msg_iovlen = 1, ++ .msg_control = buf, ++ .msg_controllen = sizeof(buf), ++ }; ++ + assert(s); + + if ((revents|EPOLLIN|EPOLLHUP) != (EPOLLIN|EPOLLHUP)) { +@@ -511,20 +522,50 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + * always leave room for a terminating NUL we might need to add. */ + limit = MIN(s->allocated - 1, s->server->line_max); + +- l = read(s->fd, s->buffer + s->length, limit - s->length); ++ iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length); ++ ++ l = recvmsg(s->fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); + if (l < 0) { +- if (errno == EAGAIN) ++ if (IN_SET(errno, EINTR, EAGAIN)) + return 0; + + log_warning_errno(errno, "Failed to read from stream: %m"); + goto terminate; + } ++ cmsg_close_all(&msghdr); + + if (l == 0) { + stdout_stream_scan(s, true); + goto terminate; + } + ++ CMSG_FOREACH(cmsg, &msghdr) ++ if (cmsg->cmsg_level == SOL_SOCKET && ++ cmsg->cmsg_type == SCM_CREDENTIALS && ++ cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred))) { ++ ucred = (struct ucred *)CMSG_DATA(cmsg); ++ break; ++ } ++ ++ /* Invalidate the context if the pid of the sender changed. ++ * This happens when a forked process inherits stdout / stderr ++ * from a parent. In this case getpeercred returns the ucred ++ * of the parent, which can be invalid if the parent has exited ++ * in the meantime. ++ */ ++ if (ucred && ucred->pid != s->ucred.pid) { ++ /* force out any previously half-written lines from a ++ * different process, before we switch to the new ucred ++ * structure for everything we just added */ ++ r = stdout_stream_scan(s, true); ++ if (r < 0) ++ goto terminate; ++ ++ s->ucred = *ucred; ++ client_context_release(s->server, s->context); ++ s->context = NULL; ++ } ++ + s->length += l; + r = stdout_stream_scan(s, false); + if (r < 0) +@@ -562,6 +603,10 @@ int stdout_stream_install(Server *s, int fd, StdoutStream **ret) { + if (r < 0) + return log_error_errno(r, "Failed to determine peer credentials: %m"); + ++ r = setsockopt_int(fd, SOL_SOCKET, SO_PASSCRED, true); ++ if (r < 0) ++ return log_error_errno(r, "SO_PASSCRED failed: %m"); ++ + if (mac_selinux_use()) { + r = getpeersec(fd, &stream->label); + if (r < 0 && r != -EOPNOTSUPP) +diff --git a/test/TEST-04-JOURNAL/test-journal.sh b/test/TEST-04-JOURNAL/test-journal.sh +index 4e539aa..de27eb0 100755 +--- a/test/TEST-04-JOURNAL/test-journal.sh ++++ b/test/TEST-04-JOURNAL/test-journal.sh +@@ -74,6 +74,22 @@ cmp /expected /output + { journalctl -ball -b -m 2>&1 || :; } | head -1 > /output + cmp /expected /output + ++# https://github.com/systemd/systemd/issues/13708 ++ID=$(systemd-id128 new) ++systemd-cat -t "$ID" bash -c 'echo parent; (echo child) & wait' & ++PID=$! ++wait %% ++journalctl --sync ++# We can drop this grep when https://github.com/systemd/systemd/issues/13937 ++# has a fix. ++journalctl -b -o export -t "$ID" --output-fields=_PID | grep '^_PID=' >/output ++[[ `grep -c . /output` -eq 2 ]] ++grep -q "^_PID=$PID" /output ++grep -vq "^_PID=$PID" /output ++ ++# Add new tests before here, the journald restarts below ++# may make tests flappy. ++ + # Don't lose streams on restart + systemctl start forever-print-hola + sleep 3 +-- +1.8.3.1 + diff --git a/0082-journald-rework-end-of-line-marker-handling-to-use-a.patch b/0082-journald-rework-end-of-line-marker-handling-to-use-a.patch new file mode 100644 index 0000000000000000000000000000000000000000..c365959cee0d5d3ce3cf46991f0b0778a927e7e5 --- /dev/null +++ b/0082-journald-rework-end-of-line-marker-handling-to-use-a.patch @@ -0,0 +1,77 @@ +From 549b7379ba404c33fd448d2bca46a57f6529b00b Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 12 May 2020 18:53:35 +0200 +Subject: [PATCH] journald: rework end of line marker handling to use a field + table + +--- + src/journal/journald-stream.c | 29 ++++++++++++++++++++--------- + 1 file changed, 20 insertions(+), 9 deletions(-) + +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index 22a70ce..b86ed78 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -57,6 +57,8 @@ typedef enum LineBreak { + LINE_BREAK_NUL, + LINE_BREAK_LINE_MAX, + LINE_BREAK_EOF, ++ _LINE_BREAK_MAX, ++ _LINE_BREAK_INVALID = -1, + } LineBreak; + + struct StdoutStream { +@@ -236,7 +238,11 @@ fail: + return log_error_errno(r, "Failed to save stream data %s: %m", s->state_file); + } + +-static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_break) { ++static int stdout_stream_log( ++ StdoutStream *s, ++ const char *p, ++ LineBreak line_break) { ++ + struct iovec *iovec; + int priority; + char syslog_priority[] = "PRIORITY=\0"; +@@ -248,6 +254,9 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea + assert(s); + assert(p); + ++ assert(line_break >= 0); ++ assert(line_break < _LINE_BREAK_MAX); ++ + if (s->context) + (void) client_context_maybe_refresh(s->server, s->context, NULL, NULL, 0, NULL, USEC_INFINITY); + else if (pid_is_valid(s->ucred.pid)) { +@@ -299,17 +308,19 @@ static int stdout_stream_log(StdoutStream *s, const char *p, LineBreak line_brea + iovec[n++] = IOVEC_MAKE_STRING(syslog_identifier); + } + +- if (line_break != LINE_BREAK_NEWLINE) { +- const char *c; ++ static const char * const line_break_field_table[_LINE_BREAK_MAX] = { ++ [LINE_BREAK_NEWLINE] = NULL, /* Do not add field if traditional newline */ ++ [LINE_BREAK_NUL] = "_LINE_BREAK=nul", ++ [LINE_BREAK_LINE_MAX] = "_LINE_BREAK=line-max", ++ [LINE_BREAK_EOF] = "_LINE_BREAK=eof", ++ }; + +- /* If this log message was generated due to an uncommon line break then mention this in the log +- * entry */ ++ const char *c = line_break_field_table[line_break]; + +- c = line_break == LINE_BREAK_NUL ? "_LINE_BREAK=nul" : +- line_break == LINE_BREAK_LINE_MAX ? "_LINE_BREAK=line-max" : +- "_LINE_BREAK=eof"; ++ /* If this log message was generated due to an uncommon line break then mention this in the log ++ * entry */ ++ if (c) + iovec[n++] = IOVEC_MAKE_STRING(c); +- } + + message = strjoin("MESSAGE=", p); + if (message) +-- +1.8.3.1 + diff --git a/0083-journald-rework-pid-change-handling.patch b/0083-journald-rework-pid-change-handling.patch new file mode 100644 index 0000000000000000000000000000000000000000..b8edce42a7c41cad06b805e7873551596225f98c --- /dev/null +++ b/0083-journald-rework-pid-change-handling.patch @@ -0,0 +1,230 @@ +From 45ba1ea5e9264d385fa565328fe957ef1d78caa1 Mon Sep 17 00:00:00 2001 +From: Lennart Poettering +Date: Tue, 12 May 2020 18:56:34 +0200 +Subject: [PATCH] journald: rework pid change handling + +Let's introduce an explicit line ending marker for line endings due to +pid change. + +Let's also make sure we don't get confused with buffer management. + +Fixes: #15654 +--- + src/journal/journald-stream.c | 108 +++++++++++++++++++++++++++--------------- + 1 file changed, 69 insertions(+), 39 deletions(-) + +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index b86ed78..3219b14 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -57,6 +57,7 @@ typedef enum LineBreak { + LINE_BREAK_NUL, + LINE_BREAK_LINE_MAX, + LINE_BREAK_EOF, ++ LINE_BREAK_PID_CHANGE, + _LINE_BREAK_MAX, + _LINE_BREAK_INVALID = -1, + } LineBreak; +@@ -313,6 +314,7 @@ static int stdout_stream_log( + [LINE_BREAK_NUL] = "_LINE_BREAK=nul", + [LINE_BREAK_LINE_MAX] = "_LINE_BREAK=line-max", + [LINE_BREAK_EOF] = "_LINE_BREAK=eof", ++ [LINE_BREAK_PID_CHANGE] = "_LINE_BREAK=pid-change", + }; + + const char *c = line_break_field_table[line_break]; +@@ -434,21 +436,43 @@ static int stdout_stream_line(StdoutStream *s, char *p, LineBreak line_break) { + assert_not_reached("Unknown stream state"); + } + +-static int stdout_stream_scan(StdoutStream *s, bool force_flush) { +- char *p; +- size_t remaining; ++static int stdout_stream_found( ++ StdoutStream *s, ++ char *p, ++ size_t l, ++ LineBreak line_break) { ++ ++ char saved; + int r; + + assert(s); ++ assert(p); ++ ++ /* Let's NUL terminate the specified buffer for this call, and revert back afterwards */ ++ saved = p[l]; ++ p[l] = 0; ++ r = stdout_stream_line(s, p, line_break); ++ p[l] = saved; + +- p = s->buffer; +- remaining = s->length; ++ return r; ++} ++ ++static int stdout_stream_scan( ++ StdoutStream *s, ++ char *p, ++ size_t remaining, ++ LineBreak force_flush, ++ size_t *ret_consumed) { + +- /* XXX: This function does nothing if (s->length == 0) */ ++ size_t consumed = 0; ++ int r; ++ ++ assert(s); ++ assert(p); + + for (;;) { + LineBreak line_break; +- size_t skip; ++ size_t skip, found; + char *end1, *end2; + + end1 = memchr(p, '\n', remaining); +@@ -456,43 +480,40 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) { + + if (end2) { + /* We found a NUL terminator */ +- skip = end2 - p + 1; ++ found = end2 - p; ++ skip = found + 1; + line_break = LINE_BREAK_NUL; + } else if (end1) { + /* We found a \n terminator */ +- *end1 = 0; +- skip = end1 - p + 1; ++ found = end1 - p; ++ skip = found + 1; + line_break = LINE_BREAK_NEWLINE; + } else if (remaining >= s->server->line_max) { + /* Force a line break after the maximum line length */ +- *(p + s->server->line_max) = 0; +- skip = remaining; ++ found = skip = s->server->line_max; + line_break = LINE_BREAK_LINE_MAX; + } else + break; + +- r = stdout_stream_line(s, p, line_break); ++ r = stdout_stream_found(s, p, found, line_break); + if (r < 0) + return r; + +- remaining -= skip; + p += skip; ++ consumed += skip; ++ remaining -= skip; + } + +- if (force_flush && remaining > 0) { +- p[remaining] = 0; +- r = stdout_stream_line(s, p, LINE_BREAK_EOF); ++ if (force_flush >= 0 && remaining > 0) { ++ r = stdout_stream_found(s, p, remaining, force_flush); + if (r < 0) + return r; + +- p += remaining; +- remaining = 0; ++ consumed += remaining; + } + +- if (p > s->buffer) { +- memmove(s->buffer, p, remaining); +- s->length = remaining; +- } ++ if (ret_consumed) ++ *ret_consumed = consumed; + + return 0; + } +@@ -500,11 +521,12 @@ static int stdout_stream_scan(StdoutStream *s, bool force_flush) { + static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, void *userdata) { + uint8_t buf[CMSG_SPACE(sizeof(struct ucred))]; + StdoutStream *s = userdata; ++ size_t limit, consumed; + struct ucred *ucred = NULL; + struct cmsghdr *cmsg; + struct iovec iovec; +- size_t limit; + ssize_t l; ++ char *p; + int r; + + struct msghdr msghdr = { +@@ -532,7 +554,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + /* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also, + * always leave room for a terminating NUL we might need to add. */ + limit = MIN(s->allocated - 1, s->server->line_max); +- ++ assert(s->length <= limit); + iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length); + + l = recvmsg(s->fd, &msghdr, MSG_DONTWAIT|MSG_CMSG_CLOEXEC); +@@ -546,7 +568,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + cmsg_close_all(&msghdr); + + if (l == 0) { +- stdout_stream_scan(s, true); ++ (void) stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_EOF, NULL); + goto terminate; + } + +@@ -558,30 +580,38 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + break; + } + +- /* Invalidate the context if the pid of the sender changed. +- * This happens when a forked process inherits stdout / stderr +- * from a parent. In this case getpeercred returns the ucred +- * of the parent, which can be invalid if the parent has exited +- * in the meantime. +- */ ++ /* Invalidate the context if the PID of the sender changed. This happens when a forked process ++ * inherits stdout/stderr from a parent. In this case getpeercred() returns the ucred of the parent, ++ * which can be invalid if the parent has exited in the meantime. */ + if (ucred && ucred->pid != s->ucred.pid) { +- /* force out any previously half-written lines from a ++ /* Force out any previously half-written lines from a + * different process, before we switch to the new ucred + * structure for everything we just added */ +- r = stdout_stream_scan(s, true); ++ r = stdout_stream_scan(s, s->buffer, s->length, /* force_flush = */ LINE_BREAK_PID_CHANGE, NULL); + if (r < 0) + goto terminate; + +- s->ucred = *ucred; +- client_context_release(s->server, s->context); +- s->context = NULL; ++ s->context = client_context_release(s->server, s->context); ++ ++ p = s->buffer + s->length; ++ } else { ++ p = s->buffer; ++ l += s->length; + } + +- s->length += l; +- r = stdout_stream_scan(s, false); ++ /* Always copy in the new credentials */ ++ if (ucred) ++ s->ucred = *ucred; ++ ++ r = stdout_stream_scan(s, p, l, _LINE_BREAK_INVALID, &consumed); + if (r < 0) + goto terminate; + ++ /* Move what wasn't consumed to the front of the buffer */ ++ assert(consumed <= (size_t) l); ++ s->length = l - consumed; ++ memmove(s->buffer, p + consumed, s->length); ++ + return 1; + + terminate: +-- +1.8.3.1 + diff --git a/0084-journald-enforce-longer-line-length-limit-during-set.patch b/0084-journald-enforce-longer-line-length-limit-during-set.patch new file mode 100644 index 0000000000000000000000000000000000000000..71aeae5198114534cfffe6642ff316eb6846ea07 --- /dev/null +++ b/0084-journald-enforce-longer-line-length-limit-during-set.patch @@ -0,0 +1,104 @@ +From 4e071b5240a29842bc8acd0d7eb0b797f2812b8b Mon Sep 17 00:00:00 2001 +From: rpm-build +Date: Fri, 21 May 2021 17:55:38 +0800 +Subject: [PATCH] change + +--- + src/journal/journald-stream.c | 35 ++++++++++++++++++++++++++++------- + 1 file changed, 28 insertions(+), 7 deletions(-) + +diff --git a/src/journal/journald-stream.c b/src/journal/journald-stream.c +index 3219b14..fda75fb 100644 +--- a/src/journal/journald-stream.c ++++ b/src/journal/journald-stream.c +@@ -38,6 +38,12 @@ + + #define STDOUT_STREAMS_MAX 4096 + ++/* During the "setup" protocol phase of the stream logic let's define a different maximum line length than ++ * during the actual operational phase. We want to allow users to specify very short line lengths after all, ++ * but the unit name we embed in the setup protocol might be longer than that. Hence, during the setup phase ++ * let's enforce a line length matching the maximum unit name length (255) */ ++#define STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX (UNIT_NAME_MAX-1U) ++ + typedef enum StdoutStreamState { + STDOUT_STREAM_IDENTIFIER, + STDOUT_STREAM_UNIT_ID, +@@ -46,7 +52,7 @@ typedef enum StdoutStreamState { + STDOUT_STREAM_FORWARD_TO_SYSLOG, + STDOUT_STREAM_FORWARD_TO_KMSG, + STDOUT_STREAM_FORWARD_TO_CONSOLE, +- STDOUT_STREAM_RUNNING ++ STDOUT_STREAM_RUNNING, + } StdoutStreamState; + + /* The different types of log record terminators: a real \n was read, a NUL character was read, the maximum line length +@@ -457,6 +463,18 @@ static int stdout_stream_found( + return r; + } + ++static size_t stdout_stream_line_max(StdoutStream *s) { ++ assert(s); ++ ++ /* During the "setup" phase of our protocol, let's ensure we use a line length where a full unit name ++ * can fit in */ ++ if (s->state != STDOUT_STREAM_RUNNING) ++ return STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX; ++ ++ /* After the protocol's "setup" phase is complete, let's use whatever the user configured */ ++ return s->server->line_max; ++} ++ + static int stdout_stream_scan( + StdoutStream *s, + char *p, +@@ -464,19 +482,22 @@ static int stdout_stream_scan( + LineBreak force_flush, + size_t *ret_consumed) { + +- size_t consumed = 0; ++ size_t consumed = 0, line_max; + int r; + + assert(s); + assert(p); + ++ line_max = stdout_stream_line_max(s); ++ + for (;;) { + LineBreak line_break; + size_t skip, found; + char *end1, *end2; ++ size_t tmp_remaining = MIN(remaining, line_max); + +- end1 = memchr(p, '\n', remaining); +- end2 = memchr(p, 0, end1 ? (size_t) (end1 - p) : remaining); ++ end1 = memchr(p, '\n', tmp_remaining); ++ end2 = memchr(p, 0, end1 ? (size_t) (end1 - p) : tmp_remaining); + + if (end2) { + /* We found a NUL terminator */ +@@ -488,9 +509,9 @@ static int stdout_stream_scan( + found = end1 - p; + skip = found + 1; + line_break = LINE_BREAK_NEWLINE; +- } else if (remaining >= s->server->line_max) { ++ } else if (remaining >= line_max) { + /* Force a line break after the maximum line length */ +- found = skip = s->server->line_max; ++ found = skip = line_max; + line_break = LINE_BREAK_LINE_MAX; + } else + break; +@@ -553,7 +574,7 @@ static int stdout_stream_process(sd_event_source *es, int fd, uint32_t revents, + + /* Try to make use of the allocated buffer in full, but never read more than the configured line size. Also, + * always leave room for a terminating NUL we might need to add. */ +- limit = MIN(s->allocated - 1, s->server->line_max); ++ limit = MIN(s->allocated - 1, MAX(s->server->line_max, STDOUT_STREAM_SETUP_PROTOCOL_LINE_MAX)); + assert(s->length <= limit); + iovec = IOVEC_MAKE(s->buffer + s->length, limit - s->length); + +-- +1.8.3.1 + diff --git a/systemd.spec b/systemd.spec index 9d73db73d9bd181bfe99ecf5b559bb1d91c1bc43..8b9db75a16e4b29d4f4a913f39717e40da4b5a39 100644 --- a/systemd.spec +++ b/systemd.spec @@ -16,7 +16,7 @@ Name: systemd Url: https://www.freedesktop.org/wiki/Software/systemd Version: 243 -Release: 36 +Release: 37 License: MIT and LGPLv2+ and GPLv2+ Summary: System and Service Manager @@ -125,6 +125,11 @@ Patch0078: 0078-backport-varlink-make-userdata-pointer-inheritance-from-var Patch0079: 0079-backport-udev-net_id-parse-_SUN-ACPI-index-as-a-signed-intege.patch Patch0080: 0080-backport-udev-net_id-don-t-generate-slot-based-names-if-multi.patch +Patch0081: 0081-journal-refresh-cached-credentials-of-stdout-streams.patch +Patch0082: 0082-journald-rework-end-of-line-marker-handling-to-use-a.patch +Patch0083: 0083-journald-rework-pid-change-handling.patch +Patch0084: 0084-journald-enforce-longer-line-length-limit-during-set.patch + #openEuler Patch9002: 1509-fix-journal-file-descriptors-leak-problems.patch Patch9003: 1602-activation-service-must-be-restarted-when-reactivated.patch @@ -1509,28 +1514,31 @@ fi %exclude /usr/share/man/man3/* %changelog -* Mon May 31 2021 overweight - 246-36 +* Mon May 31 2021 overweight - 243-37 +- fix journald: enforce longer line length limit during "setup" phase of stream protocol + +* Mon May 31 2021 overweight - 243-36 - fix patches name and patches num -* Thu May 27 2021 shenyangyang - 246-35 +* Thu May 27 2021 shenyangyang - 243-35 - Type:bugfix - ID:NA - SUG:NA - DESC:change requires to openssl-libs as post scripts systemctl requires libssl.so.1.1 -* Mon May 10 2021 shenyangyang - 246-34 +* Mon May 10 2021 shenyangyang - 243-34 - Type:bugfix - ID:NA - SUG:NA - DESC:backport from upstream to solve the problem when devices claim the same slot -* Fri Apr 02 2021 fangxiuning - 246-33 +* Fri Apr 02 2021 fangxiuning - 243-33 - Type:bugfix - ID:NA - SUG:NA - DESC:fix userdate double free -* Fri Jan 29 2021 overweight - 246-32 +* Fri Jan 29 2021 overweight - 243-32 - Type:cve - ID:CVE-2018-21029 - SUG:NA