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,6 +216,8 @@ static inline uint32_t vnc_has_feature(VncState *vs, int feature) { | ||
216 | */ | 216 | */ |
217 | 217 | ||
218 | static void vnc_update_client(void *opaque); | 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 | static void vnc_colordepth(VncState *vs); | 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,9 +654,6 @@ static void send_framebuffer_update(VncState *vs, int x, int y, int w, int h) | ||
652 | 654 | ||
653 | static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, int w, int h) | 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 | vnc_write_u8(vs, 0); /* msg id */ | 657 | vnc_write_u8(vs, 0); /* msg id */ |
659 | vnc_write_u8(vs, 0); | 658 | vnc_write_u8(vs, 0); |
660 | vnc_write_u16(vs, 1); /* number of rects */ | 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,13 +666,22 @@ static void vnc_copy(VncState *vs, int src_x, int src_y, int dst_x, int dst_y, i | ||
667 | static void vnc_dpy_copy(DisplayState *ds, int src_x, int src_y, int dst_x, int dst_y, int w, int h) | 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 | VncDisplay *vd = ds->opaque; | 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 | if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) | 681 | if (vnc_has_feature(vs, VNC_FEATURE_COPYRECT)) |
673 | vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h); | 682 | vnc_copy(vs, src_x, src_y, dst_x, dst_y, w, h); |
674 | else /* TODO */ | 683 | else /* TODO */ |
675 | vnc_update(vs, dst_x, dst_y, w, h); | 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,6 +806,8 @@ static void vnc_update_client(void *opaque) | ||
798 | 806 | ||
799 | if (vs->csock != -1) { | 807 | if (vs->csock != -1) { |
800 | qemu_mod_timer(vs->timer, qemu_get_clock(rt_clock) + VNC_REFRESH_INTERVAL); | 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,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 | int vnc_client_io_error(VncState *vs, int ret, int last_errno) | 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,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 | return 0; | 944 | return 0; |
923 | } | 945 | } |
@@ -927,7 +949,8 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno) | @@ -927,7 +949,8 @@ int vnc_client_io_error(VncState *vs, int ret, int last_errno) | ||
927 | 949 | ||
928 | void vnc_client_error(VncState *vs) | 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,16 +1133,21 @@ void vnc_client_read(void *opaque) | ||
1110 | else | 1133 | else |
1111 | #endif /* CONFIG_VNC_SASL */ | 1134 | #endif /* CONFIG_VNC_SASL */ |
1112 | ret = vnc_client_read_plain(vs); | 1135 | ret = vnc_client_read_plain(vs); |
1113 | - if (!ret) | 1136 | + if (!ret) { |
1137 | + if (vs->csock == -1) | ||
1138 | + vnc_disconnect_finish(vs); | ||
1114 | return; | 1139 | return; |
1140 | + } | ||
1115 | 1141 | ||
1116 | while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { | 1142 | while (vs->read_handler && vs->input.offset >= vs->read_handler_expect) { |
1117 | size_t len = vs->read_handler_expect; | 1143 | size_t len = vs->read_handler_expect; |
1118 | int ret; | 1144 | int ret; |
1119 | 1145 | ||
1120 | ret = vs->read_handler(vs, vs->input.buffer, len); | 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 | return; | 1149 | return; |
1150 | + } | ||
1123 | 1151 | ||
1124 | if (!ret) { | 1152 | if (!ret) { |
1125 | memmove(vs->input.buffer, vs->input.buffer + len, (vs->input.offset - len)); | 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,7 +1162,7 @@ void vnc_write(VncState *vs, const void *data, size_t len) | ||
1134 | { | 1162 | { |
1135 | buffer_reserve(&vs->output, len); | 1163 | buffer_reserve(&vs->output, len); |
1136 | 1164 | ||
1137 | - if (buffer_empty(&vs->output)) { | 1165 | + if (vs->csock != -1 && buffer_empty(&vs->output)) { |
1138 | qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); | 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,7 +1203,7 @@ void vnc_write_u8(VncState *vs, uint8_t value) | ||
1175 | 1203 | ||
1176 | void vnc_flush(VncState *vs) | 1204 | void vnc_flush(VncState *vs) |
1177 | { | 1205 | { |
1178 | - if (vs->output.offset) | 1206 | + if (vs->csock != -1 && vs->output.offset) |
1179 | vnc_client_write(vs); | 1207 | vnc_client_write(vs); |
1180 | } | 1208 | } |
1181 | 1209 | ||
@@ -2009,11 +2037,13 @@ static void vnc_connect(VncDisplay *vd, int csock) | @@ -2009,11 +2037,13 @@ static void vnc_connect(VncDisplay *vd, int csock) | ||
2009 | vnc_write(vs, "RFB 003.008\n", 12); | 2037 | vnc_write(vs, "RFB 003.008\n", 12); |
2010 | vnc_flush(vs); | 2038 | vnc_flush(vs); |
2011 | vnc_read_when(vs, protocol_version, 12); | 2039 | vnc_read_when(vs, protocol_version, 12); |
2012 | - vnc_update_client(vs); | ||
2013 | reset_keys(vs); | 2040 | reset_keys(vs); |
2014 | 2041 | ||
2015 | vs->next = vd->clients; | 2042 | vs->next = vd->clients; |
2016 | vd->clients = vs; | 2043 | vd->clients = vs; |
2044 | + | ||
2045 | + vnc_update_client(vs); | ||
2046 | + /* vs might be free()ed here */ | ||
2017 | } | 2047 | } |
2018 | 2048 | ||
2019 | static void vnc_listen_read(void *opaque) | 2049 | static void vnc_listen_read(void *opaque) |