add 'buffersu' program to wrap rsync and make sure there is no wedging

when SuperSU is combined with rsync
sigsegv_dump
Greg Alexander 9 years ago
parent 2349aef08c
commit a916ac5c08

20
NOTES

@ -176,5 +176,25 @@ SIOCGIFCONF, just like ifconfig does, but it works fine with
java.net.NetworkInterface.
June 20, 2015.
At some points, rsync is only write()ing, and assumes that the other end
will receive it all. The other end does a little bit of write()ing of
its own, and then is happy to read() all it wants. So this written stuff
may sit in a buffer somewhere indefinitely. If that happens, an
infelicity in the design of SuperSU causes everything to wedge.
Of course, this is only if you set the shell to /system/xbin/su as a way
of having root access for rsync.
Anyways, I made a new program, "buffersu", which is just a
stdin/stdout-buffering wrapper for rsync() that is guaranteed to always
perform any read() that is possible at any time, no matter how many
write()s are outstanding. That seems to do the trick.
XXX - document rsync buffer and supersu integration
XXX - new release
XXX - if you remove it from the recent apps list, does it stop the service??
XXX - support password-based logins?

@ -2,6 +2,7 @@ ndk-build -j8 &&
mv libs/armeabi/scp libs/armeabi/libscp.so &&
mv libs/armeabi/sftp-server libs/armeabi/libsftp-server.so &&
mv libs/armeabi/rsync libs/armeabi/librsync.so &&
mv libs/armeabi/buffersu libs/armeabi/libbuffersu.so &&
ant release &&
pbinst bin/SimpleSSHD-release.apk
# cat bin/SimpleSSHD-release.apk | ssh roach 'cat > /sdcard/buh.apk; source .profile; am start --user 0 -d file:///sdcard/buh.apk'

@ -587,7 +587,11 @@ void run_shell_command(const char* cmd, unsigned int maxfd, char* usershell) {
cmd = t;
} else if (cmd && !strncmp(cmd, "rsync ", 6)) {
char *t = malloc(strlen(cmd)+strlen(NDK_EXECUTABLES_PATH)+80);
sprintf(t, "%s/lib%s.so %s", NDK_EXECUTABLES_PATH, "rsync",
char *x = "rsync";
if (conf_rsyncbuffer) {
x = "buffersu";
}
sprintf(t, "%s/lib%s.so %s", NDK_EXECUTABLES_PATH, x,
cmd+6);
cmd = t;
}

@ -597,3 +597,17 @@ LOCAL_LDLIBS :=
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)
# build separate sftp executable
include $(CLEAR_VARS)
LOCAL_CFLAGS :=
LOCAL_MODULE := buffersu
LOCAL_SRC_FILES := buffersu.c
# LOCAL_LDLIBS :=
LOCAL_LDFLAGS := -static
include $(BUILD_EXECUTABLE)

@ -0,0 +1,217 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/select.h>
#include <signal.h>
#include <fcntl.h>
#include <sys/wait.h>
#define WRAPPED_CMD "/data/data/org.galexander.sshd/lib/librsync.so"
#define WRAPPED_ARG0 "rsync"
static void
die(void)
{
int status;
waitpid(-1, &status, WNOHANG);
exit(0);
}
static volatile int child_dead = 0;
static volatile pid_t child_pid = 0;
static void
sigchld(int sig)
{
int status;
waitpid(-1, &status, WNOHANG);
/* don't exit, in case child has unprocessed output */
child_dead = 1;
}
static void
sigpipe(int sig)
{
kill(child_pid, SIGPIPE);
die();
}
#define BLKSZ 1024
struct block {
struct block *next;
struct block *prev;
char buf[BLKSZ];
int ofs,len;
};
struct buf {
struct block *head; /* read here */
struct block *tail; /* write here */
};
static int
buf_waiting(struct buf *b)
{
return (b->head != NULL);
}
static int
buf_read(struct buf *b, int fd)
{
struct block *p;
char t[BLKSZ];
int n;
n = read(fd, t, BLKSZ);
if (n < 0) {
perror("read");
die();
}
if (n == 0) {
/* EOF */
return 0;
}
p = malloc(sizeof *p);
memcpy(p->buf, t, BLKSZ);
p->next = NULL;
p->prev = b->tail;
if (b->tail) {
b->tail->next = p;
}
p->ofs = 0;
p->len = n;
b->tail = p;
if (!b->head) {
b->head = p;
}
return n;
}
static void
buf_write(struct buf *b, int fd)
{
struct block *p;
int n;
if (!b->head) {
return;
}
p = b->head;
n = write(fd, p->buf+p->ofs, p->len);
if (n <= 0) {
perror("write");
return;
}
p->ofs += n;
p->len -= n;
if (p->len <= 0) {
if (p->next) {
b->head = p->next;
p->next->prev = NULL;
} else {
b->head = NULL;
b->tail = NULL;
}
free(p);
}
}
int
main(int argc, char **argv)
{
int p0[2], p1[2];
pid_t pid;
pipe(p0);
pipe(p1);
if ((pid=fork())) {
/* parent */
fd_set ifds, ofds;
int child_stdin, child_stdout;
struct buf buf0 = { 0 };
struct buf buf1 = { 0 };
int nfd = 2;
close(p0[0]);
close(p1[1]);
if (pid == -1) {
perror("fork");
return -1;
}
child_pid = pid;
signal(SIGPIPE, sigpipe);
signal(SIGCHLD, sigchld);
child_stdin = p0[1];
child_stdout = p1[0];
if (child_stdin > nfd) nfd = child_stdin;
if (child_stdout > nfd) nfd = child_stdout;
nfd++;
while (1) {
int s;
FD_ZERO(&ifds);
FD_SET(0, &ifds);
FD_SET(child_stdout, &ifds);
FD_ZERO(&ofds);
if (buf_waiting(&buf1)) {
FD_SET(1, &ofds);
} else if (child_dead) {
die();
}
if (!child_dead && buf_waiting(&buf0)) {
FD_SET(child_stdin, &ofds);
}
s = select(nfd, &ifds, &ofds, NULL, NULL);
if (s < 0) {
perror("select");
}
#if 0
fprintf(stderr, "select %d\n", s);
#define T(x) \
if (FD_ISSET(0, &x##s)) fprintf(stderr, #x " 0\n"); \
if (FD_ISSET(1, &x##s)) fprintf(stderr, #x " 1\n"); \
if (FD_ISSET(child_stdin, &x##s)) fprintf(stderr, #x " child_stdin\n"); \
if (FD_ISSET(child_stdout, &x##s)) fprintf(stderr, #x " child_stdout\n");
T(ifd)
T(ofd)
#endif
if (FD_ISSET(0, &ifds)) {
buf_read(&buf0, 0);
}
if (FD_ISSET(child_stdout, &ifds)) {
if (!buf_read(&buf1, child_stdout)) {
child_dead = 1;
}
}
if (FD_ISSET(1, &ofds)) {
buf_write(&buf1, 1);
}
if (!child_dead && FD_ISSET(child_stdin, &ofds)) {
buf_write(&buf0, child_stdin);
}
}
die();
} else {
/* child */
char **child_argv;
close(0);
close(1);
dup2(p0[0], 0);
dup2(p1[1], 1);
close(p0[0]);
close(p0[1]);
close(p1[0]);
close(p1[1]);
child_argv = malloc((argc+1) * sizeof *child_argv);
memcpy(child_argv, argv, argc*sizeof *child_argv);
child_argv[0] = WRAPPED_ARG0;
child_argv[argc] = NULL;
execv(WRAPPED_CMD, child_argv);
perror("execv");
return -1;
}
return 0;
}

@ -38,6 +38,7 @@ extern const char *conf_path;
extern const char *conf_shell;
extern const char *conf_home;
const char *conf_path_file(const char *fn);
extern int conf_rsyncbuffer;
#endif /* __CONFIG_H__ */

@ -10,6 +10,7 @@
#include <ctype.h>
const char *conf_path = "", *conf_shell = "", *conf_home = "";
int conf_rsyncbuffer = 0;
/* NB - this will leak memory like crazy if called often.... */
const char *
@ -117,7 +118,8 @@ from_java_string(jobject s)
JNIEXPORT void JNICALL
Java_org_galexander_sshd_SimpleSSHDService_start_1sshd(JNIEnv *env_,
jclass cl,
jint port, jobject jpath, jobject jshell, jobject jhome, jobject jextra)
jint port, jobject jpath, jobject jshell, jobject jhome, jobject jextra,
jint rsyncbuffer)
{
pid_t pid;
const char *extra;
@ -129,6 +131,7 @@ Java_org_galexander_sshd_SimpleSSHDService_start_1sshd(JNIEnv *env_,
conf_shell = from_java_string(jshell);
conf_home = from_java_string(jhome);
extra = from_java_string(jextra);
conf_rsyncbuffer = rsyncbuffer;
pid = fork();
if (pid == 0) {

@ -20,6 +20,10 @@
android:title="Login Shell"
android:summary="Location of sh binary"
android:defaultValue="/system/bin/sh" />
<CheckBoxPreference
android:key="rsyncbuffer"
android:title="Buffer hack for rsync+SuperSU"
android:defaultValue="false" />
<EditTextPreference
android:key="home"
android:title="Home Directory"

@ -16,6 +16,9 @@ public class Prefs {
public static boolean get_onboot() {
return pref.getBoolean("onboot", false);
}
public static boolean get_rsyncbuffer() {
return pref.getBoolean("rsyncbuffer", false);
}
public static int get_port() {
int ret;
try {

@ -65,7 +65,8 @@ public class SimpleSSHDService extends Service {
}
start_sshd(Prefs.get_port(),
Prefs.get_path(), Prefs.get_shell(),
Prefs.get_home(), Prefs.get_extra());
Prefs.get_home(), Prefs.get_extra(),
(Prefs.get_rsyncbuffer() ? 1 : 0));
if (sshd_pid != 0) {
final int pid = sshd_pid;
@ -99,7 +100,8 @@ public class SimpleSSHDService extends Service {
}
private static native void start_sshd(int port, String path,
String shell, String home, String extra);
String shell, String home, String extra,
int rsyncbuffer);
private static native void stop_sshd();
private static native int waitpid(int pid);
static {

Loading…
Cancel
Save