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) | ... | ... |