Commit 198a0039c5fca224a77e9761e2350dd9cc102ad0

Authored by Gerd Hoffmann
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
... ... @@ -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)
... ...