Skip to content

bug(cp): --attributes-only, there was no self file check it can lead to race conditions #13161

Description

@HackingRepo

Hi, uutils mainteners

we confirmed that the --attributes-only cp option do not have a check at all for the same files, as we confirmed

relunsec@relunsec:~/software/coreutils/target/debug$ strace ./cp --attributes-only a a
execve("./cp", ["./cp", "--attributes-only", "a", "a"], 0x7ffe45bae8f8 /* 74 vars */) = 0
brk(NULL)                               = 0x5d5c03c66000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x78cd43a98000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=97781, ...}) = 0
mmap(NULL, 97781, PROT_READ, MAP_PRIVATE, 3, 0) = 0x78cd43a80000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=187120, ...}) = 0
mmap(NULL, 185256, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x78cd43a52000
mmap(0x78cd43a56000, 147456, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x4000) = 0x78cd43a56000
mmap(0x78cd43a7a000, 16384, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x78cd43a7a000
mmap(0x78cd43a7e000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x2c000) = 0x78cd43a7e000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0\0\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1198376, ...}) = 0
mmap(NULL, 1200152, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x78cd4392c000
mmap(0x78cd4393e000, 655360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x12000) = 0x78cd4393e000
mmap(0x78cd439de000, 466944, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xb2000) = 0x78cd439de000
mmap(0x78cd43a50000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x123000) = 0x78cd43a50000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \250\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 840, 64) = 840
fstat(3, {st_mode=S_IFREG|0755, st_size=2186512, ...}) = 0
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 840, 64) = 840
mmap(NULL, 2231696, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x78cd4370b000
mmap(0x78cd43733000, 1671168, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x28000) = 0x78cd43733000
mmap(0x78cd438cb000, 319488, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x78cd438cb000
mmap(0x78cd43919000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x20d000) = 0x78cd43919000
mmap(0x78cd4391f000, 52624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x78cd4391f000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x78cd43709000
arch_prctl(ARCH_SET_FS, 0x78cd43709d80) = 0
set_tid_address(0x78cd4370a3a8)         = 87544
set_robust_list(0x78cd4370a060, 24)     = 0
rseq(0x78cd43709ac0, 0x21, 0, 0x53053053) = 0
mprotect(0x78cd43919000, 16384, PROT_READ) = 0
mprotect(0x78cd43a50000, 4096, PROT_READ) = 0
mprotect(0x78cd43a7e000, 4096, PROT_READ) = 0
mprotect(0x5d5bfa7c3000, 196608, PROT_READ) = 0
mprotect(0x78cd43add000, 8192, PROT_READ) = 0
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
getrandom("\xe6\x83\xce\xd5\xb1\x64\x61\x45", 8, GRND_NONBLOCK) = 8
munmap(0x78cd43a80000, 97781)           = 0
fcntl(0, F_GETFD)                       = 0
fcntl(1, F_GETFD)                       = 0
fcntl(2, F_GETFD)                       = 0
rt_sigaction(SIGPIPE, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
poll([{fd=0, events=0}, {fd=1, events=0}, {fd=2, events=0}], 3, 0) = 0 (Timeout)
rt_sigaction(SIGPIPE, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x78cd43750cb0}, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
brk(NULL)                               = 0x5d5c03c66000
brk(0x5d5c03c87000)                     = 0x5d5c03c87000
openat(AT_FDCWD, "/proc/self/maps", O_RDONLY|O_CLOEXEC) = 3
prlimit64(0, RLIMIT_STACK, NULL, {rlim_cur=8192*1024, rlim_max=RLIM64_INFINITY}) = 0
fstat(3, {st_mode=S_IFREG|0444, st_size=0, ...}) = 0
read(3, "5d5bfa372000-5d5bfa511000 r--p 0"..., 1024) = 1024
read(3, "r/lib/x86_64-linux-gnu/libc.so.6"..., 1024) = 1024
read(3, "           /usr/lib/x86_64-linux"..., 1024) = 1024
read(3, "nux-gnu/ld-linux-x86-64.so.2\n78c"..., 1024) = 363
lseek(3, 0, SEEK_CUR)                   = 3435
lseek(3, 3351, SEEK_SET)                = 3351
close(3)                                = 0
sched_getaffinity(87544, 32, [0 1 2 3 4 5 6 7]) = 8
rt_sigaction(SIGSEGV, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
mmap(NULL, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_STACK, -1, 0) = 0x78cd43a95000
mprotect(0x78cd43a95000, 4096, PROT_NONE) = 0
sigaltstack({ss_sp=0x78cd43a96000, ss_flags=0, ss_size=8192}, NULL) = 0
gettid()                                = 87544
rt_sigaction(SIGSEGV, {sa_handler=0x5d5bfa7a1710, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x78cd43750cb0}, NULL, 8) = 0
rt_sigaction(SIGBUS, NULL, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=0}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=0x5d5bfa7a1710, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x78cd43750cb0}, NULL, 8) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/../uu/cp/locales", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=4, ...}) = 0
readlink("/home", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/src", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/src/uucore", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
faccessat2(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/", F_OK, AT_EACCESS) = 0
readlink("/home/relunsec/software/coreutils/src/uu", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/src/uu/cp", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/src/uu/cp/locales", 0x7fff37a2d570, 1023) = -1 EINVAL (Invalid argument)
statx(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/locales", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=4, ...}) = 0
openat(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/locales/en-US.ftl", O_RDONLY|O_CLOEXEC) = 3
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=4113, ...}) = 0
read(3, "# Common strings shared across a"..., 4113) = 4113
read(3, "", 32)                         = 0
close(3)                                = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/../uu/cp/locales", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=4, ...}) = 0
openat(AT_FDCWD, "/home/relunsec/software/coreutils/src/uucore/../uu/cp/locales/en-US.ftl", O_RDONLY|O_CLOEXEC) = 3
statx(3, "", AT_STATX_SYNC_AS_STAT|AT_EMPTY_PATH, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=8247, ...}) = 0
read(3, "cp-about = Copy SOURCE to DEST, "..., 8247) = 8247
read(3, "", 32)                         = 0
close(3)                                = 0
brk(0x5d5c03ca9000)                     = 0x5d5c03ca9000
rt_sigaction(SIGPIPE, {sa_handler=SIG_DFL, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x78cd43750cb0}, {sa_handler=SIG_IGN, sa_mask=[PIPE], sa_flags=SA_RESTORER|SA_RESTART, sa_restorer=0x78cd43750cb0}, 8) = 0
rt_sigaction(SIGSEGV, {sa_handler=SIG_DFL, sa_mask=~[RTMIN RT_1], sa_flags=SA_RESTORER, sa_restorer=0x78cd43750cb0}, {sa_handler=0x5d5bfa7a1710, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x78cd43750cb0}, 8) = 0
rt_sigaction(SIGBUS, {sa_handler=SIG_DFL, sa_mask=~[RTMIN RT_1], sa_flags=SA_RESTORER, sa_restorer=0x78cd43750cb0}, {sa_handler=0x5d5bfa7a1710, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK|SA_SIGINFO, sa_restorer=0x78cd43750cb0}, 8) = 0
ioctl(1, TCGETS2, {c_iflag=BRKINT|IGNPAR|ICRNL|IXON|IMAXBEL, c_oflag=NL0|CR0|TAB0|BS0|VT0|FF0|OPOST|ONLCR, c_cflag=B38400|CS8|CREAD, c_lflag=ISIG|ICANON|ECHO|ECHOE|ECHOK|IEXTEN|ECHOCTL|ECHOKE, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
rt_sigprocmask(SIG_BLOCK, ~[], [], 8)   = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_DROPPABLE|MAP_ANONYMOUS, -1, 0) = 0x78cd43a94000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x78cd43a93000
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
getrandom("\x3c\x1b\xba\x04\x85\x19\x4c\x05\xc1\xce\xf8\x85\x40\x2c\xfc\x72\x82\x99\x1a\xc1\xb0\x3a\x4f\x45\x50\xa3\x43\x53\xc5\x7e\x04\x45", 32, 0) = 32
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
openat(AT_FDCWD, "a", O_WRONLY|O_CREAT|O_CLOEXEC, 0666) = 3
fcntl(3, F_GETFD)                       = 0x1 (flags FD_CLOEXEC)
close(3)                                = 0
chmod("a", 0100664)                     = 0
getcwd("/home/relunsec/software/coreutils/target/debug", 512) = 47
readlink("/home", 0x7fff37a2ae10, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec", 0x7fff37a2ae10, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software", 0x7fff37a2ae10, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils", 0x7fff37a2ae10, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/target", 0x7fff37a2ae10, 1023) = -1 EINVAL (Invalid argument)
readlink("/home/relunsec/software/coreutils/target/debug", 0x7fff37a2ae10, 1023) = -1 EINVAL (Invalid argument)
statx(AT_FDCWD, "/", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=STATX_ATTR_MOUNT_ROOT, stx_mode=S_IFDIR|0755, stx_size=29, ...}) = 0
statx(AT_FDCWD, "/home", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=STATX_ATTR_MOUNT_ROOT, stx_mode=S_IFDIR|0755, stx_size=8, ...}) = 0
statx(AT_FDCWD, "/home/relunsec", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0755, stx_size=275, ...}) = 0
statx(AT_FDCWD, "/home/relunsec/software", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=39, ...}) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=42, ...}) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/target", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=5, ...}) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/target/debug", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFDIR|0775, stx_size=448, ...}) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/target/debug/a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/target/debug/a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/target/debug/a", AT_STATX_SYNC_AS_STAT, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
statx(AT_FDCWD, "/home/relunsec/software/coreutils/target/debug/a", AT_STATX_SYNC_AS_STAT|AT_SYMLINK_NOFOLLOW, STATX_ALL, {stx_mask=STATX_ALL|STATX_MNT_ID, stx_attributes=0, stx_mode=S_IFREG|0664, stx_size=6, ...}) = 0
newfstatat(AT_FDCWD, "a", {st_mode=S_IFREG|0664, st_size=6, ...}, 0) = 0
sigaltstack({ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=8192}, NULL) = 0
munmap(0x78cd43a95000, 12288)           = 0
exit_group(0)                           = ?
+++ exited with 0 +++
relunsec@relunsec:~/software/coreutils/target/debug$ 
relunsec@relunsec:~/software/coreutils/target/debug$ gnucp --attributes-only a a
gnucp: 'a' and 'a' are the same file
relunsec@relunsec:~/software/coreutils/target/debug$ gnucp --attributes-only a ./a
gnucp: 'a' and './a' are the same file
relunsec@relunsec:~/software/coreutils/target/debug$ 

while the gnu one have

and very critically, look

openat(AT_FDCWD, "a", O_WRONLY|O_CREAT|O_CLOEXEC, 0666) = 3
chmod("a", 0100664) = 0

chmod called against a file path not the fd 3, it can lead to race conditions

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions