Compare commits
13 Commits
ci-hygiene
...
ci-codeql
| Author | SHA1 | Date | |
|---|---|---|---|
| 5393ddf795 | |||
| 25b58aad4d | |||
| c71eea8e68 | |||
| afbdf92fa6 | |||
| daac34f7c8 | |||
| 88692b79eb | |||
| 035dcde9ba | |||
| cc14ae1c79 | |||
| 78f807b3dd | |||
| fd885f023c | |||
| 033a1d48f5 | |||
| 82285f9c28 | |||
| 43b2708186 |
@@ -0,0 +1,10 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: weekly
|
||||
groups:
|
||||
github-actions:
|
||||
patterns:
|
||||
- "*"
|
||||
@@ -7,45 +7,137 @@ on:
|
||||
workflow_dispatch: # this is a nice option that will enable a button w/ inputs
|
||||
inputs:
|
||||
git-ref:
|
||||
description: Git Ref (Optional)
|
||||
description: Git Ref (Optional)
|
||||
required: false
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build-and-test:
|
||||
name: Build and test
|
||||
runs-on: ubuntu-latest
|
||||
name: ${{ matrix.compiler }} / ${{ matrix.buildtype }}
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
compiler: [gcc, clang]
|
||||
buildtype: [debugoptimized, release]
|
||||
include:
|
||||
- compiler: gcc
|
||||
cc: gcc
|
||||
- compiler: clang
|
||||
cc: clang
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- uses: actions/setup-python@v4
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install valgrind gcc ninja-build meson libglib2.0-dev libfuse3-dev
|
||||
sudo apt-get install -y gcc clang ninja-build libglib2.0-dev libfuse3-dev openssh-server openssh-client fuse3
|
||||
|
||||
- name: Install meson
|
||||
run: pip3 install meson pytest
|
||||
run: pip3 install meson pytest pytest-timeout
|
||||
|
||||
- name: build
|
||||
- name: Print tool versions
|
||||
run: |
|
||||
mkdir build; cd build
|
||||
meson ..
|
||||
ninja
|
||||
${{ matrix.cc }} --version
|
||||
meson --version
|
||||
|
||||
# cd does not persist across steps
|
||||
- name: upload build artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
- name: Setup SSH
|
||||
run: |
|
||||
mkdir -p ~/.ssh
|
||||
chmod 700 ~/.ssh
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -q -N ""
|
||||
cat ~/.ssh/id_ed25519.pub > ~/.ssh/authorized_keys
|
||||
chmod 600 ~/.ssh/authorized_keys
|
||||
sudo systemctl start ssh || sudo service ssh start
|
||||
ssh -o StrictHostKeyChecking=no -o BatchMode=yes localhost true
|
||||
|
||||
- name: Check FUSE availability
|
||||
run: |
|
||||
test -e /dev/fuse
|
||||
command -v fusermount3
|
||||
|
||||
- name: Build
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
run: |
|
||||
meson setup build --buildtype=${{ matrix.buildtype }} -Dwerror=true
|
||||
ninja -C build
|
||||
|
||||
- name: Upload build artifact
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
with:
|
||||
name: sshfs
|
||||
name: sshfs-${{ matrix.compiler }}-${{ matrix.buildtype }}
|
||||
path: build/sshfs
|
||||
if-no-files-found: ignore
|
||||
|
||||
- name: make ssh into localhost without prompt possible for tests
|
||||
run: |
|
||||
ssh-keygen -b 2048 -t rsa -f ~/.ssh/id_rsa -q -N ""
|
||||
cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys
|
||||
|
||||
- name: run tests
|
||||
- name: Run tests
|
||||
timeout-minutes: 20
|
||||
run: |
|
||||
cd build
|
||||
python3 -m pytest test/
|
||||
python3 -m pytest --maxfail=99 --timeout=300 --junitxml=test-results.xml test/
|
||||
|
||||
- name: Upload test results
|
||||
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
||||
if: always()
|
||||
with:
|
||||
name: test-results-${{ matrix.compiler }}-${{ matrix.buildtype }}
|
||||
path: |
|
||||
build/test-results.xml
|
||||
build/meson-logs/
|
||||
|
||||
strict-warnings:
|
||||
name: ${{ matrix.compiler }} / strict warnings
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- compiler: gcc
|
||||
cc: gcc
|
||||
extra_cflags: "-Wformat=2 -Wformat-security -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wnull-dereference"
|
||||
- compiler: clang
|
||||
cc: clang
|
||||
extra_cflags: "-Wformat=2 -Wformat-security -Wundef -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes -Wnull-dereference"
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc clang ninja-build libglib2.0-dev libfuse3-dev
|
||||
|
||||
- name: Install meson
|
||||
run: pip3 install meson
|
||||
|
||||
- name: Print tool versions
|
||||
run: |
|
||||
${{ matrix.cc }} --version
|
||||
meson --version
|
||||
|
||||
- name: Build with strict warnings
|
||||
env:
|
||||
CC: ${{ matrix.cc }}
|
||||
CFLAGS: ${{ matrix.extra_cflags }}
|
||||
run: |
|
||||
meson setup build -Dwerror=true
|
||||
ninja -C build
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
name: CodeQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze C
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@bc0b696b4103f5fe60f15749af68a046868d511a # codeql-bundle-v2.25.4
|
||||
with:
|
||||
languages: c-cpp
|
||||
build-mode: manual
|
||||
queries: security-extended
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc ninja-build pkg-config libglib2.0-dev libfuse3-dev
|
||||
pip3 install meson
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
meson setup build --buildtype=debug
|
||||
ninja -C build sshfs
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@bc0b696b4103f5fe60f15749af68a046868d511a # codeql-bundle-v2.25.4
|
||||
@@ -280,6 +280,11 @@ struct read_chunk {
|
||||
struct sshfs_io sio;
|
||||
};
|
||||
|
||||
struct conntab_entry {
|
||||
unsigned refcount;
|
||||
struct conn *conn;
|
||||
};
|
||||
|
||||
struct sshfs_file {
|
||||
struct buffer handle;
|
||||
struct list_head write_reqs;
|
||||
@@ -289,15 +294,11 @@ struct sshfs_file {
|
||||
off_t next_pos;
|
||||
int is_seq;
|
||||
struct conn *conn;
|
||||
struct conntab_entry *ce;
|
||||
int connver;
|
||||
int modifver;
|
||||
};
|
||||
|
||||
struct conntab_entry {
|
||||
unsigned refcount;
|
||||
struct conn *conn;
|
||||
};
|
||||
|
||||
struct sshfs {
|
||||
char *directport;
|
||||
char *ssh_command;
|
||||
@@ -1441,23 +1442,24 @@ static int do_read(struct conn *conn, struct buffer *buf)
|
||||
|
||||
static int sftp_read(struct conn *conn, uint8_t *type, struct buffer *buf)
|
||||
{
|
||||
int res;
|
||||
int res = -1;
|
||||
struct buffer buf2;
|
||||
uint32_t len;
|
||||
buf_init(&buf2, 5);
|
||||
res = do_read(conn, &buf2);
|
||||
if (res != -1) {
|
||||
if (buf_get_uint32(&buf2, &len) == -1)
|
||||
return -1;
|
||||
if (len > MAX_REPLY_LEN) {
|
||||
fprintf(stderr, "reply len too large: %u\n", len);
|
||||
return -1;
|
||||
}
|
||||
if (buf_get_uint8(&buf2, type) == -1)
|
||||
return -1;
|
||||
buf_init(buf, len - 1);
|
||||
res = do_read(conn, buf);
|
||||
if (do_read(conn, &buf2) == -1)
|
||||
goto out;
|
||||
if (buf_get_uint32(&buf2, &len) == -1)
|
||||
goto out;
|
||||
if (len < 1 || len > MAX_REPLY_LEN) {
|
||||
fprintf(stderr, "bad reply len: %u\n", len);
|
||||
goto out;
|
||||
}
|
||||
if (buf_get_uint8(&buf2, type) == -1)
|
||||
goto out;
|
||||
buf_init(buf, len - 1);
|
||||
res = do_read(conn, buf);
|
||||
|
||||
out:
|
||||
buf_free(&buf2);
|
||||
return res;
|
||||
}
|
||||
@@ -1530,10 +1532,14 @@ static int process_one_request(struct conn *conn)
|
||||
|
||||
buf_init(&buf, 0);
|
||||
res = sftp_read(conn, &type, &buf);
|
||||
if (res == -1)
|
||||
if (res == -1) {
|
||||
buf_free(&buf);
|
||||
return -1;
|
||||
if (buf_get_uint32(&buf, &id) == -1)
|
||||
}
|
||||
if (buf_get_uint32(&buf, &id) == -1) {
|
||||
buf_free(&buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&sshfs.lock);
|
||||
req = (struct request *)
|
||||
@@ -2763,6 +2769,12 @@ static int sshfs_utimens(const char *path, const struct timespec tv[2],
|
||||
return err;
|
||||
}
|
||||
|
||||
static gboolean conntab_entry_is(gpointer key, gpointer value, gpointer data)
|
||||
{
|
||||
(void) key;
|
||||
return value == data;
|
||||
}
|
||||
|
||||
static int sshfs_open_common(const char *path, mode_t mode,
|
||||
struct fuse_file_info *fi)
|
||||
{
|
||||
@@ -2823,12 +2835,13 @@ static int sshfs_open_common(const char *path, mode_t mode,
|
||||
g_hash_table_insert(sshfs.conntab, g_strdup(path), ce);
|
||||
}
|
||||
sf->conn = ce->conn;
|
||||
sf->ce = ce;
|
||||
ce->refcount++;
|
||||
sf->conn->file_count++;
|
||||
assert(sf->conn->file_count > 0);
|
||||
} else {
|
||||
sf->conn = &sshfs.conns[0];
|
||||
ce = NULL; // only to silence compiler warning
|
||||
sf->ce = NULL;
|
||||
}
|
||||
sf->connver = sf->conn->connver;
|
||||
pthread_mutex_unlock(&sshfs.lock);
|
||||
@@ -2868,10 +2881,12 @@ static int sshfs_open_common(const char *path, mode_t mode,
|
||||
if (sshfs.max_conns > 1) {
|
||||
pthread_mutex_lock(&sshfs.lock);
|
||||
sf->conn->file_count--;
|
||||
ce->refcount--;
|
||||
if(ce->refcount == 0) {
|
||||
g_hash_table_remove(sshfs.conntab, path);
|
||||
g_free(ce);
|
||||
sf->ce->refcount--;
|
||||
if (sf->ce->refcount == 0) {
|
||||
g_hash_table_foreach_remove(sshfs.conntab,
|
||||
conntab_entry_is,
|
||||
sf->ce);
|
||||
g_free(sf->ce);
|
||||
}
|
||||
pthread_mutex_unlock(&sshfs.lock);
|
||||
}
|
||||
@@ -2942,7 +2957,6 @@ static int sshfs_release(const char *path, struct fuse_file_info *fi)
|
||||
{
|
||||
struct sshfs_file *sf = get_sshfs_file(fi);
|
||||
struct buffer *handle = &sf->handle;
|
||||
struct conntab_entry *ce;
|
||||
if (sshfs_file_is_conn(sf)) {
|
||||
sshfs_flush(path, fi);
|
||||
sftp_request(sf->conn, SSH_FXP_CLOSE, handle, 0, NULL);
|
||||
@@ -2950,12 +2964,13 @@ static int sshfs_release(const char *path, struct fuse_file_info *fi)
|
||||
buf_free(handle);
|
||||
chunk_put_locked(sf->readahead);
|
||||
if (sshfs.max_conns > 1) {
|
||||
struct conntab_entry *ce = sf->ce;
|
||||
pthread_mutex_lock(&sshfs.lock);
|
||||
sf->conn->file_count--;
|
||||
ce = g_hash_table_lookup(sshfs.conntab, path);
|
||||
ce->refcount--;
|
||||
if(ce->refcount == 0) {
|
||||
g_hash_table_remove(sshfs.conntab, path);
|
||||
if (ce->refcount == 0) {
|
||||
g_hash_table_foreach_remove(sshfs.conntab,
|
||||
conntab_entry_is, ce);
|
||||
g_free(ce);
|
||||
}
|
||||
pthread_mutex_unlock(&sshfs.lock);
|
||||
@@ -3382,7 +3397,7 @@ static int sshfs_statfs(const char *path, struct statvfs *buf)
|
||||
return sshfs_ext_statvfs(path, buf);
|
||||
|
||||
buf->f_namemax = 255;
|
||||
buf->f_bsize = sshfs.blksize;
|
||||
buf->f_bsize = sshfs.blksize ? sshfs.blksize : 4096;
|
||||
/*
|
||||
* df seems to use f_bsize instead of f_frsize, so make them
|
||||
* the same
|
||||
@@ -3939,7 +3954,7 @@ static char *tokenize_on_space(char *str)
|
||||
|
||||
start = pos;
|
||||
|
||||
while (pos && *pos != '\0') {
|
||||
while (*pos != '\0') {
|
||||
// break on space, but not on '\ '
|
||||
if (*pos == ' ' && *(pos - 1) != '\\') {
|
||||
break;
|
||||
|
||||
@@ -168,6 +168,7 @@ def test_sshfs(
|
||||
tst_mkdir_exist(mnt_dir)
|
||||
tst_readdir_repeated(mnt_dir)
|
||||
tst_rename_sibling(mnt_dir)
|
||||
tst_rename_open_release(mnt_dir)
|
||||
except Exception as exc:
|
||||
cleanup(mount_process, mnt_dir)
|
||||
raise exc
|
||||
@@ -503,6 +504,23 @@ def tst_rename_sibling(mnt_dir):
|
||||
os.unlink(name_c)
|
||||
|
||||
|
||||
def tst_rename_open_release(mnt_dir):
|
||||
src = pjoin(mnt_dir, name_generator())
|
||||
dst = pjoin(mnt_dir, name_generator())
|
||||
|
||||
fd = os.open(src, os.O_CREAT | os.O_RDWR)
|
||||
try:
|
||||
os.write(fd, b"data")
|
||||
os.rename(src, dst)
|
||||
finally:
|
||||
os.close(fd)
|
||||
|
||||
assert not os.path.exists(src)
|
||||
with open(dst, "rb") as fh:
|
||||
assert fh.read() == b"data"
|
||||
os.unlink(dst)
|
||||
|
||||
|
||||
def tst_link(mnt_dir, cache_timeout):
|
||||
name1 = pjoin(mnt_dir, name_generator())
|
||||
name2 = pjoin(mnt_dir, name_generator())
|
||||
@@ -851,3 +869,44 @@ def test_direct_io(tmpdir, capfd):
|
||||
raise
|
||||
else:
|
||||
umount(mount_process, mnt_dir)
|
||||
|
||||
|
||||
def test_bad_sftp_reply_len(tmpdir):
|
||||
"""sshfs must reject a zero-length SFTP reply instead of underflowing."""
|
||||
helper = tmpdir.join("bad_sftp.py")
|
||||
helper.write(
|
||||
'#!/usr/bin/env python3\n'
|
||||
'import os, struct, sys\n'
|
||||
'def read_pkt():\n'
|
||||
' hdr = os.read(0, 4)\n'
|
||||
' if len(hdr) < 4: sys.exit(0)\n'
|
||||
' n = struct.unpack(">I", hdr)[0]\n'
|
||||
' while n:\n'
|
||||
' c = os.read(0, n)\n'
|
||||
' if not c: sys.exit(0)\n'
|
||||
' n -= len(c)\n'
|
||||
'read_pkt()\n'
|
||||
'os.write(1, struct.pack(">IBI", 5, 2, 3))\n' # SSH_FXP_VERSION v3
|
||||
'read_pkt()\n'
|
||||
'os.write(1, struct.pack(">IB", 0, 0))\n' # len=0 reply (5 bytes on wire)
|
||||
)
|
||||
helper.chmod(0o755)
|
||||
|
||||
mnt_dir = str(tmpdir.mkdir("mnt"))
|
||||
cmdline = base_cmdline + [
|
||||
pjoin(basename, "sshfs"),
|
||||
"-f",
|
||||
"dummy:/",
|
||||
mnt_dir,
|
||||
"-o", f"ssh_command={helper}",
|
||||
]
|
||||
res = subprocess.run(
|
||||
cmdline,
|
||||
stdin=subprocess.DEVNULL,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
timeout=10,
|
||||
text=True,
|
||||
)
|
||||
assert res.returncode != 0
|
||||
assert "bad reply len: 0" in res.stderr
|
||||
|
||||
Reference in New Issue
Block a user