Commit 198a0039c5fca224a77e9761e2350dd9cc102ad0
Committed by
Anthony Liguori
1 parent
11a1feb6
vnc: rework VncState release workflow.
Split socket closing and releasing of VncState into two steps. First close the socket and set the variable to -1 to indicate shutdown in progress. Do the actual release in a few places where we can be sure it doesn't cause trouble in form of use-after-free. Add some checks for a valid socket handle to make sure we don't try to use the closed socket. Signed-off-by: Gerd Hoffmann <kraxel@redhat.com> Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>
Showing
1 changed file
with
75 additions
and
45 deletions
vnc.c
| ... | ... | @@ -216,6 +216,8 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) { |
| 216 | 216 | */ |
| 217 | 217 | |
| 218 | 218 | static void vnc_update_client(void *opaque); |
| 219 | +static void vnc_disconnect_start(VncState *vs); | |
| 220 | +static void vnc_disconnect_finish(VncState *vs); | |
| 219 | 221 | |
| 220 | 222 | static void vnc_colordepth(VncState *vs); |
| 221 | 223 | |
| ... | ... | @@ -652,9 +654,6 @@ static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) |
| 652 | 654 | |
| 653 | 655 | static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h) |
| 654 | 656 | { |
| 655 | - vs->force_update = 1; | |
| 656 | - vnc_update_client(vs); | |
| 657 | - | |
| 658 | 657 | vnc_write_u8(vs, 0); /* msg id */ |
| 659 | 658 | vnc_write_u8(vs, 0); |
| 660 | 659 | vnc_write_u16(vs, 1); /* number of rects */ |
| ... | ... | @@ -667,13 +666,22 @@ static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, i |
| 667 | 666 | static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h) |
| 668 | 667 | { |
| 669 | 668 | VncDisplay *vd = ds->opaque; |
| 670 | - VncState *vs = vd->clients; | |
| 671 | - while (vs != NULL) { | |
| 669 | + VncState *vs, *vn; | |
| 670 | + | |
| 671 | + for (vs = vd->clients; vs != NULL; vs = vn) { | |
| 672 | + vn = vs->next; | |
| 673 | + if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) { | |
| 674 | + vs->force_update = 1; | |
| 675 | + vnc_update_client(vs); | |
| 676 | + /* vs might be free()ed here */ | |
| 677 | + } | |
| 678 | + } | |
| 679 | + | |
| 680 | + for (vs = vd->clients; vs != NULL; vs = vs->next) { | |
| 672 | 681 | if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) |
| 673 | 682 | vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h); |
| 674 | 683 | else /* TODO */ |
| 675 | 684 | vnc_update(vs, dst_x, dst_y, w, h); |
| 676 | - vs = vs->next; | |
| 677 | 685 | } |
| 678 | 686 | } |
| 679 | 687 | |
| ... | ... | @@ -798,6 +806,8 @@ static void vnc_update_client(void *opaque) |
| 798 | 806 | |
| 799 | 807 | if (vs->csock != -1) { |
| 800 | 808 | qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); |
| 809 | + } else { | |
| 810 | + vnc_disconnect_finish(vs); | |
| 801 | 811 | } |
| 802 | 812 | |
| 803 | 813 | } |
| ... | ... | @@ -868,6 +878,48 @@ static void audio_del(VncState *vs) |
| 868 | 878 | } |
| 869 | 879 | } |
| 870 | 880 | |
| 881 | +static void vnc_disconnect_start(VncState *vs) | |
| 882 | +{ | |
| 883 | + if (vs->csock == -1) | |
| 884 | + return; | |
| 885 | + qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); | |
| 886 | + closesocket(vs->csock); | |
| 887 | + vs->csock = -1; | |
| 888 | +} | |
| 889 | + | |
| 890 | +static void vnc_disconnect_finish(VncState *vs) | |
| 891 | +{ | |
| 892 | + qemu_del_timer(vs->timer); | |
| 893 | + qemu_free_timer(vs->timer); | |
| 894 | + if (vs->input.buffer) qemu_free(vs->input.buffer); | |
| 895 | + if (vs->output.buffer) qemu_free(vs->output.buffer); | |
| 896 | +#ifdef CONFIG_VNC_TLS | |
| 897 | + vnc_tls_client_cleanup(vs); | |
| 898 | +#endif /* CONFIG_VNC_TLS */ | |
| 899 | +#ifdef CONFIG_VNC_SASL | |
| 900 | + vnc_sasl_client_cleanup(vs); | |
| 901 | +#endif /* CONFIG_VNC_SASL */ | |
| 902 | + audio_del(vs); | |
| 903 | + | |
| 904 | + VncState *p, *parent = NULL; | |
| 905 | + for (p = vs->vd->clients; p != NULL; p = p->next) { | |
| 906 | + if (p == vs) { | |
| 907 | + if (parent) | |
| 908 | + parent->next = p->next; | |
| 909 | + else | |
| 910 | + vs->vd->clients = p->next; | |
| 911 | + break; | |
| 912 | + } | |
| 913 | + parent = p; | |
| 914 | + } | |
| 915 | + if (!vs->vd->clients) | |
| 916 | + dcl->idle = 1; | |
| 917 | + | |
| 918 | + qemu_free(vs->server.ds->data); | |
| 919 | + qemu_free(vs->server.ds); | |
| 920 | + qemu_free(vs->guest.ds); | |
| 921 | + qemu_free(vs); | |
| 922 | +} | |
| 871 | 923 | |
| 872 | 924 | int vnc_client_io_error(VncState *vs, int ret, int last_errno) |
| 873 | 925 | { |
| ... | ... | @@ -885,39 +937,9 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno) |
| 885 | 937 | } |
| 886 | 938 | } |
| 887 | 939 | |
| 888 | - VNC_DEBUG("Closing down client sock %d %d\n", ret, ret < 0 ? last_errno : 0); | |
| 889 | - qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); | |
| 890 | - closesocket(vs->csock); | |
| 891 | - qemu_del_timer(vs->timer); | |
| 892 | - qemu_free_timer(vs->timer); | |
| 893 | - if (vs->input.buffer) qemu_free(vs->input.buffer); | |
| 894 | - if (vs->output.buffer) qemu_free(vs->output.buffer); | |
| 895 | -#ifdef CONFIG_VNC_TLS | |
| 896 | - vnc_tls_client_cleanup(vs); | |
| 897 | -#endif /* CONFIG_VNC_TLS */ | |
| 898 | -#ifdef CONFIG_VNC_SASL | |
| 899 | - vnc_sasl_client_cleanup(vs); | |
| 900 | -#endif /* CONFIG_VNC_SASL */ | |
| 901 | - audio_del(vs); | |
| 902 | - | |
| 903 | - VncState *p, *parent = NULL; | |
| 904 | - for (p = vs->vd->clients; p != NULL; p = p->next) { | |
| 905 | - if (p == vs) { | |
| 906 | - if (parent) | |
| 907 | - parent->next = p->next; | |
| 908 | - else | |
| 909 | - vs->vd->clients = p->next; | |
| 910 | - break; | |
| 911 | - } | |
| 912 | - parent = p; | |
| 913 | - } | |
| 914 | - if (!vs->vd->clients) | |
| 915 | - dcl->idle = 1; | |
| 916 | - | |
| 917 | - qemu_free(vs->server.ds->data); | |
| 918 | - qemu_free(vs->server.ds); | |
| 919 | - qemu_free(vs->guest.ds); | |
| 920 | - qemu_free(vs); | |
| 940 | + VNC_DEBUG("Closing down client sock: ret %d, errno %d\n", | |
| 941 | + ret, ret < 0 ? last_errno : 0); | |
| 942 | + vnc_disconnect_start(vs); | |
| 921 | 943 | |
| 922 | 944 | return 0; |
| 923 | 945 | } |
| ... | ... | @@ -927,7 +949,8 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno) |
| 927 | 949 | |
| 928 | 950 | void vnc_client_error(VncState *vs) |
| 929 | 951 | { |
| 930 | - vnc_client_io_error(vs, -1, EINVAL); | |
| 952 | + VNC_DEBUG("Closing down client sock: protocol error\n"); | |
| 953 | + vnc_disconnect_start(vs); | |
| 931 | 954 | } |
| 932 | 955 | |
| 933 | 956 | |
| ... | ... | @@ -1110,16 +1133,21 @@ void vnc_client_read(void *opaque) |
| 1110 | 1133 | else |
| 1111 | 1134 | #endif /* CONFIG_VNC_SASL */ |
| 1112 | 1135 | ret = vnc_client_read_plain(vs); |
| 1113 | - if (!ret) | |
| 1136 | + if (!ret) { | |
| 1137 | + if (vs->csock == -1) | |
| 1138 | + vnc_disconnect_finish(vs); | |
| 1114 | 1139 | return; |
| 1140 | + } | |
| 1115 | 1141 | |
| 1116 | 1142 | while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { |
| 1117 | 1143 | size_t len = vs->read_handler_expect; |
| 1118 | 1144 | int ret; |
| 1119 | 1145 | |
| 1120 | 1146 | ret = vs->read_handler(vs, vs->input.buffer, len); |
| 1121 | - if (vs->csock == -1) | |
| 1147 | + if (vs->csock == -1) { | |
| 1148 | + vnc_disconnect_finish(vs); | |
| 1122 | 1149 | return; |
| 1150 | + } | |
| 1123 | 1151 | |
| 1124 | 1152 | if (!ret) { |
| 1125 | 1153 | memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); |
| ... | ... | @@ -1134,7 +1162,7 @@ void vnc_write(VncState *vs, const void *data, size_t len) |
| 1134 | 1162 | { |
| 1135 | 1163 | buffer_reserve(&vs->output, len); |
| 1136 | 1164 | |
| 1137 | - if (buffer_empty(&vs->output)) { | |
| 1165 | + if (vs->csock != -1 && buffer_empty(&vs->output)) { | |
| 1138 | 1166 | qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); |
| 1139 | 1167 | } |
| 1140 | 1168 | |
| ... | ... | @@ -1175,7 +1203,7 @@ void vnc_write_u8(VncState *vs, uint8_t value) |
| 1175 | 1203 | |
| 1176 | 1204 | void vnc_flush(VncState *vs) |
| 1177 | 1205 | { |
| 1178 | - if (vs->output.offset) | |
| 1206 | + if (vs->csock != -1 && vs->output.offset) | |
| 1179 | 1207 | vnc_client_write(vs); |
| 1180 | 1208 | } |
| 1181 | 1209 | |
| ... | ... | @@ -2009,11 +2037,13 @@ static void vnc_connect(VncDisplay *vd, int csock) |
| 2009 | 2037 | vnc_write(vs, "RFB 003.008\n", 12); |
| 2010 | 2038 | vnc_flush(vs); |
| 2011 | 2039 | vnc_read_when(vs, protocol_version, 12); |
| 2012 | - vnc_update_client(vs); | |
| 2013 | 2040 | reset_keys(vs); |
| 2014 | 2041 | |
| 2015 | 2042 | vs->next = vd->clients; |
| 2016 | 2043 | vd->clients = vs; |
| 2044 | + | |
| 2045 | + vnc_update_client(vs); | |
| 2046 | + /* vs might be free()ed here */ | |
| 2017 | 2047 | } |
| 2018 | 2048 | |
| 2019 | 2049 | static void vnc_listen_read(void *opaque) | ... | ... |